Swapchain presentation and GLFW
- Render objects list - SDL2 replaced with GLFW - Swapchain synchronization, presentation and recreation on resize
This commit is contained in:
parent
389ed599c8
commit
1d65039e2f
@ -10,7 +10,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
project (EtrediaEmpires)
|
||||
|
||||
add_subdirectory(materials)
|
||||
add_subdirectory(sdk/materials)
|
||||
add_subdirectory(utils)
|
||||
add_subdirectory (p7lib)
|
||||
add_subdirectory (ECSAPI)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -23,7 +23,7 @@ list(TRANSFORM SOURCES PREPEND "src/")
|
||||
|
||||
find_package(Boost REQUIRED COMPONENTS lockfree)
|
||||
find_package(xxHash CONFIG REQUIRED)
|
||||
find_package(SDL2 CONFIG REQUIRED)
|
||||
find_package(glfw3 CONFIG REQUIRED)
|
||||
find_package(Vulkan REQUIRED)
|
||||
find_package(RmlUi CONFIG REQUIRED)
|
||||
|
||||
@ -36,12 +36,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC utils)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Boost::lockfree)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE xxHash::xxhash)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE p7)
|
||||
target_link_libraries(
|
||||
${PROJECT_NAME}
|
||||
PRIVATE
|
||||
$<TARGET_NAME_IF_EXISTS:SDL2::SDL2main>
|
||||
$<IF:$<TARGET_EXISTS:SDL2::SDL2>,SDL2::SDL2,SDL2::SDL2-static>
|
||||
)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE glfw)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Vulkan::Vulkan)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE RmlUi::RmlUi)
|
||||
|
||||
|
||||
@ -25,6 +25,8 @@ struct AssetShader : AssetData
|
||||
|
||||
struct AssetMaterial : AssetData
|
||||
{
|
||||
uint32_t vertexShader;
|
||||
uint32_t fragmentShader;
|
||||
uint32_t vertexShaderAssetID;
|
||||
uint32_t fragmentShaderAssetID;
|
||||
uint32_t renderType;
|
||||
uint32_t renderStage;
|
||||
};
|
||||
@ -85,9 +85,6 @@ public:
|
||||
void Start();
|
||||
void Shutdown();
|
||||
|
||||
void Request(const AssetStreamCommand &command);
|
||||
void Request(const AssetStreamCommand::Type &type);
|
||||
|
||||
void LoadShaders();
|
||||
|
||||
void LoadAsync(AssetInfo::Type type, const std::filesystem::path &path);
|
||||
@ -115,6 +112,8 @@ private:
|
||||
std::vector<AssetMaterial*> materialsStorage;
|
||||
|
||||
void Threading();
|
||||
void Request(const AssetStreamCommand &command);
|
||||
void Request(const AssetStreamCommand::Type &type);
|
||||
void Loop();
|
||||
void ProcessCommand(AssetStreamCommand &command);
|
||||
void Release();
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include "AssetData.hpp"
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#define PIPELINE_CREATE_NEW 0
|
||||
#define PIPELINE_CREATE_DERIVED (1 << 0)
|
||||
@ -25,11 +26,6 @@ struct PipelineConfig
|
||||
VkRect2D defaultScissors;
|
||||
std::vector<VkDynamicState> dynamicStates;
|
||||
|
||||
std::vector<VkAttachmentDescription> colorAttachmentConfigs;
|
||||
std::vector<VkAttachmentReference> colorAttachmentRefs;
|
||||
std::vector<VkSubpassDescription> subpassConfigs;
|
||||
VkRenderPassCreateInfo renderPassConfig;
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineConfig;
|
||||
|
||||
PipelineConfig Copy() const;
|
||||
@ -39,26 +35,26 @@ struct Pipeline
|
||||
{
|
||||
VkPipeline instance;
|
||||
VkPipelineLayout layout;
|
||||
VkRenderPass renderPass;
|
||||
VkViewport viewport;
|
||||
VkRect2D scissors;
|
||||
};
|
||||
|
||||
class PipelineFactory
|
||||
{
|
||||
public:
|
||||
PipelineFactory(const AssetMaterial &baseMaterial, VkDevice &device, VkExtent2D &surfaceExtent, VkFormat &surfaceFormat);
|
||||
PipelineFactory(const AssetMaterial &baseMaterial, const VkRenderPass &baseRenderPass, VkDevice &device, VkExtent2D &surfaceExtent);
|
||||
~PipelineFactory();
|
||||
|
||||
PipelineConfig &Request(const AssetMaterial &material, uint32_t flags);
|
||||
Pipeline& Get(const AssetMaterial &material);
|
||||
void Create(const AssetMaterial &material, const VkRenderPass &renderPass, const uint32_t &flags);
|
||||
|
||||
private:
|
||||
PipelineConfig basePipelineConfig;
|
||||
|
||||
Pipeline basePipeline;
|
||||
std::vector<Pipeline> pipelines;
|
||||
std::unordered_map<const AssetMaterial*, Pipeline> pipelines;
|
||||
|
||||
VkDevice &device;
|
||||
VkExtent2D &surfaceExtent;
|
||||
VkFormat &surfaceFormat;
|
||||
|
||||
void Create(const AssetMaterial &material, uint32_t flags);
|
||||
};
|
||||
@ -2,11 +2,12 @@
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <Boost/lockfree/spsc_queue.hpp>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <RmlUi/Core.h>
|
||||
|
||||
#include "PipelineFactory.hpp"
|
||||
@ -24,16 +25,46 @@
|
||||
|
||||
#define QUEUE_FAMILY_NO_VALUE UINT32_MAX
|
||||
|
||||
#define RENDER_PASS_BASE 438461662
|
||||
#define RENDER_PASS_GUI
|
||||
|
||||
struct Transform
|
||||
{
|
||||
float x = 0.0f, y = 0.0f;
|
||||
float scaleX = 1.0f, scaleY = 1.0f, scaleZ = 1.0f;
|
||||
};
|
||||
|
||||
struct Mesh
|
||||
{
|
||||
uint32_t vertexCount = 0;
|
||||
};
|
||||
|
||||
struct RenderObject
|
||||
{
|
||||
// Multiple transforms for batching
|
||||
std::vector<Transform> transforms;
|
||||
uint8_t depth = 0;
|
||||
Mesh mesh; //TODO: mesh (vertex/index buffer)
|
||||
//TODO: texture
|
||||
|
||||
AssetMaterial &material;
|
||||
//TODO: materialParams (uniforms)
|
||||
};
|
||||
bool operator < (const RenderObject& lhs, const RenderObject& rhs);
|
||||
|
||||
struct RenderCommand
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
NONE,
|
||||
|
||||
//STORE screenHeight and screenWidth BEFORE request
|
||||
RESIZE_WINDOW,
|
||||
REGISTER_OBJECTS,
|
||||
RESIZE_WINDOW, //STORE screenHeight and screenWidth BEFORE request
|
||||
SHUTDOWN
|
||||
} type = NONE;
|
||||
|
||||
RenderObject *objects;
|
||||
size_t objectsSize;
|
||||
};
|
||||
|
||||
struct QueueFamiliesBundle
|
||||
@ -48,6 +79,44 @@ struct QueuesBundle
|
||||
VkQueue present;
|
||||
};
|
||||
|
||||
struct DrawConfig
|
||||
{
|
||||
VkCommandBufferBeginInfo commandBeginConfig {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.pInheritanceInfo = nullptr
|
||||
};
|
||||
|
||||
VkClearValue clearColor {{{1.0f, 1.0f, 1.0f, 1.0f}}};
|
||||
|
||||
VkRenderPassBeginInfo renderPassBeginConfig {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.pNext = nullptr,
|
||||
.clearValueCount = 1,
|
||||
.pClearValues = &clearColor
|
||||
};
|
||||
|
||||
VkPipelineStageFlags waitImageStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
|
||||
VkSubmitInfo submitConfig {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.pNext = nullptr,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitDstStageMask = &waitImageStage,
|
||||
.commandBufferCount = 1,
|
||||
.signalSemaphoreCount = 1
|
||||
};
|
||||
|
||||
VkPresentInfoKHR presentConfig {
|
||||
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.waitSemaphoreCount = 1,
|
||||
.swapchainCount = 1,
|
||||
.pResults = nullptr
|
||||
};
|
||||
};
|
||||
|
||||
struct SurfaceInfo
|
||||
{
|
||||
VkSurfaceCapabilitiesKHR capabilities;
|
||||
@ -65,44 +134,67 @@ public:
|
||||
std::atomic<bool> isRunning;
|
||||
std::atomic<int> screenWidth, screenHeight;
|
||||
|
||||
Renderer(SDL_Window *window, const AssetMaterial& baseMaterial);
|
||||
Renderer(GLFWwindow *window, const AssetMaterial& baseMaterial);
|
||||
~Renderer();
|
||||
|
||||
void Start();
|
||||
void Shutdown();
|
||||
|
||||
void Request(const RenderCommand &command);
|
||||
void Request(const RenderCommand::Type &type);
|
||||
void RegisterObjects(RenderObject* objects, size_t size);
|
||||
void ResizeWindow(int width, int height);
|
||||
|
||||
private:
|
||||
std::stop_token *stoken;
|
||||
std::jthread renderThread;
|
||||
boost::lockfree::spsc_queue<RenderCommand, boost::lockfree::capacity<512>> commands;
|
||||
|
||||
SDL_Window *window;
|
||||
GLFWwindow *window;
|
||||
|
||||
VkInstance vulkan;
|
||||
VkSurfaceKHR surface;
|
||||
VkPhysicalDevice gpu;
|
||||
VkDevice device;
|
||||
VkSwapchainKHR swapchain;
|
||||
VkRenderPass baseRenderPass;
|
||||
VkCommandPool commandPool = nullptr;
|
||||
std::vector<VkCommandBuffer> commandBuffers;
|
||||
std::vector<VkImage> frames;
|
||||
std::vector<VkImageView> frameViews;
|
||||
std::vector<VkFramebuffer> baseFrameBuffers;
|
||||
std::vector<VkSemaphore> imageAvailable;
|
||||
std::vector<VkSemaphore> renderFinished;
|
||||
std::vector<VkFence> inFlightFences;
|
||||
std::vector<VkFence> imagesInFlight;
|
||||
|
||||
SurfaceInfo surfaceInfo;
|
||||
QueueFamiliesBundle queueFamilies;
|
||||
QueuesBundle queues;
|
||||
const float defaultQueuePriority = 1.0f;
|
||||
const AssetMaterial &baseMaterial;
|
||||
DrawConfig drawConfig;
|
||||
PipelineFactory *pipelineFactory;
|
||||
size_t currentFrame = 0;
|
||||
const float DEFAULT_QUEUE_PRIORITY = 1.0f;
|
||||
const AssetMaterial &baseMaterial;
|
||||
const size_t MAX_FRAMES_IN_FLIGHT = 2;
|
||||
|
||||
std::set<RenderObject> objects;
|
||||
|
||||
Rml::Context *uiContext;
|
||||
|
||||
void InitVulkan();
|
||||
void SetupDevice();
|
||||
void SetupSwapchain();
|
||||
void RecreateSwapchain();
|
||||
void ReleaseSwapchain();
|
||||
void SetupRenderPasses();
|
||||
void SetupBuffers();
|
||||
void SetupSync();
|
||||
|
||||
void Draw();
|
||||
|
||||
void Threading();
|
||||
void Request(const RenderCommand &command);
|
||||
void Request(const RenderCommand::Type &type);
|
||||
void Init();
|
||||
void Loop();
|
||||
void ProcessCommand(const RenderCommand &command);
|
||||
void Release();
|
||||
|
||||
@ -10,13 +10,6 @@ PipelineConfig PipelineConfig::Copy() const
|
||||
copy.viewportStateConfig.pScissors = ©.defaultScissors;
|
||||
copy.dynamicStateConfig.pDynamicStates = copy.dynamicStates.data();
|
||||
copy.colorBlendConfig.pAttachments = copy.colorBlendAttachmentConfigs.data();
|
||||
copy.renderPassConfig.pAttachments = copy.colorAttachmentConfigs.data();
|
||||
copy.renderPassConfig.pSubpasses = copy.subpassConfigs.data();
|
||||
for (VkSubpassDescription &config : copy.subpassConfigs)
|
||||
{
|
||||
config.pColorAttachments = copy.colorAttachmentRefs.data();
|
||||
}
|
||||
|
||||
copy.pipelineConfig.pVertexInputState = ©.vertexInputConfig;
|
||||
copy.pipelineConfig.pInputAssemblyState = ©.inputAssemblyConfig;
|
||||
copy.pipelineConfig.pViewportState = ©.viewportStateConfig;
|
||||
@ -29,10 +22,16 @@ PipelineConfig PipelineConfig::Copy() const
|
||||
return copy;
|
||||
}
|
||||
|
||||
PipelineFactory::PipelineFactory(const AssetMaterial &baseMaterial, VkDevice &device, VkExtent2D &surfaceExtent, VkFormat &surfaceFormat) :
|
||||
device(device), surfaceExtent(surfaceExtent), surfaceFormat(surfaceFormat)
|
||||
PipelineFactory::PipelineFactory(
|
||||
const AssetMaterial &baseMaterial,
|
||||
const VkRenderPass &baseRenderPass,
|
||||
VkDevice &device,
|
||||
VkExtent2D &surfaceExtent
|
||||
) :
|
||||
device(device), surfaceExtent(surfaceExtent)
|
||||
{
|
||||
basePipelineConfig.dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT);
|
||||
basePipelineConfig.dynamicStates.push_back(VK_DYNAMIC_STATE_SCISSOR);
|
||||
|
||||
basePipelineConfig.colorBlendAttachmentConfigs.push_back({
|
||||
.blendEnable = VK_TRUE,
|
||||
@ -45,27 +44,6 @@ PipelineFactory::PipelineFactory(const AssetMaterial &baseMaterial, VkDevice &de
|
||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT
|
||||
});
|
||||
|
||||
basePipelineConfig.colorAttachmentConfigs.push_back({
|
||||
.format = surfaceFormat,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT, // TODO: Multisampling
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
|
||||
});
|
||||
basePipelineConfig.colorAttachmentRefs.push_back({
|
||||
.attachment = 0,
|
||||
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
});
|
||||
|
||||
basePipelineConfig.subpassConfigs.push_back({
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.colorAttachmentCount = static_cast<uint32_t>(basePipelineConfig.colorAttachmentRefs.size()),
|
||||
.pColorAttachments = basePipelineConfig.colorAttachmentRefs.data()
|
||||
});
|
||||
|
||||
basePipelineConfig.vertexInputConfig = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@ -152,15 +130,6 @@ PipelineFactory::PipelineFactory(const AssetMaterial &baseMaterial, VkDevice &de
|
||||
.pAttachments = basePipelineConfig.colorBlendAttachmentConfigs.data(),
|
||||
};
|
||||
|
||||
basePipelineConfig.renderPassConfig = {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.attachmentCount = static_cast<uint32_t>(basePipelineConfig.colorAttachmentConfigs.size()),
|
||||
.pAttachments = basePipelineConfig.colorAttachmentConfigs.data(),
|
||||
.subpassCount = static_cast<uint32_t>(basePipelineConfig.subpassConfigs.size()),
|
||||
.pSubpasses = basePipelineConfig.subpassConfigs.data()
|
||||
};
|
||||
|
||||
basePipelineConfig.pipelineConfig = {
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@ -172,17 +141,23 @@ PipelineFactory::PipelineFactory(const AssetMaterial &baseMaterial, VkDevice &de
|
||||
.pDepthStencilState = nullptr,
|
||||
.pColorBlendState = &basePipelineConfig.colorBlendConfig,
|
||||
.pDynamicState = &basePipelineConfig.dynamicStateConfig,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = -1
|
||||
};
|
||||
|
||||
LOG(INFO, "Creating base material pipeline...");
|
||||
Create(baseMaterial, PIPELINE_CREATE_NEW);
|
||||
Create(baseMaterial, baseRenderPass, PIPELINE_CREATE_NEW);
|
||||
LOG(INFO, "Base material pipeline created");
|
||||
}
|
||||
|
||||
void PipelineFactory::Create(const AssetMaterial &material, uint32_t flags)
|
||||
Pipeline& PipelineFactory::Get(const AssetMaterial &material)
|
||||
{
|
||||
if (pipelines.contains(&material))
|
||||
return pipelines[&material];
|
||||
else return basePipeline;
|
||||
}
|
||||
|
||||
void PipelineFactory::Create(const AssetMaterial &material, const VkRenderPass &renderPass, const uint32_t &flags)
|
||||
{
|
||||
Pipeline newPipeline {};
|
||||
Pipeline *result = &basePipeline;
|
||||
@ -209,11 +184,6 @@ void PipelineFactory::Create(const AssetMaterial &material, uint32_t flags)
|
||||
LOG(CRITICAL, "Failed to create pipeline layout");
|
||||
config->pipelineConfig.layout = result->layout;
|
||||
|
||||
vkResult = vkCreateRenderPass(device, &config->renderPassConfig, nullptr, &result->renderPass);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(CRITICAL, "Failed to create render pass");
|
||||
config->pipelineConfig.renderPass = result->renderPass;
|
||||
|
||||
// Configure shaders
|
||||
AssetHandle shaderHandle;
|
||||
AssetShader *shader;
|
||||
@ -224,7 +194,7 @@ void PipelineFactory::Create(const AssetMaterial &material, uint32_t flags)
|
||||
|
||||
// Vertex
|
||||
VkShaderModule vertexModule;
|
||||
shaderHandle = Engine::assetManager->Find(material.vertexShader);
|
||||
shaderHandle = Engine::assetManager->Find(material.vertexShaderAssetID);
|
||||
shader = &Engine::assetManager->Get<AssetShader>(shaderHandle);
|
||||
shaderModuleCreateConfig = {
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
@ -246,7 +216,7 @@ void PipelineFactory::Create(const AssetMaterial &material, uint32_t flags)
|
||||
|
||||
// Fragment
|
||||
VkShaderModule fragmentModule;
|
||||
shaderHandle = Engine::assetManager->Find(material.fragmentShader);
|
||||
shaderHandle = Engine::assetManager->Find(material.fragmentShaderAssetID);
|
||||
shader = &Engine::assetManager->Get<AssetShader>(shaderHandle);
|
||||
shaderModuleCreateConfig = {
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
@ -268,6 +238,8 @@ void PipelineFactory::Create(const AssetMaterial &material, uint32_t flags)
|
||||
|
||||
config->pipelineConfig.stageCount = static_cast<uint32_t>(stagesConfigs.size());
|
||||
config->pipelineConfig.pStages = stagesConfigs.data();
|
||||
config->pipelineConfig.renderPass = renderPass;
|
||||
config->pipelineConfig.subpass = material.renderStage;
|
||||
|
||||
vkResult = vkCreateGraphicsPipelines(
|
||||
device,
|
||||
@ -281,7 +253,10 @@ void PipelineFactory::Create(const AssetMaterial &material, uint32_t flags)
|
||||
LOG(CRITICAL, "Failed to create pipeline");
|
||||
|
||||
if (flags & PIPELINE_CREATE_DERIVED)
|
||||
pipelines.push_back(newPipeline);
|
||||
pipelines[&material] = newPipeline;
|
||||
|
||||
result->viewport = config->defaultViewport;
|
||||
result->scissors = config->defaultScissors;
|
||||
|
||||
vkDestroyShaderModule(device, vertexModule, nullptr);
|
||||
vkDestroyShaderModule(device, fragmentModule, nullptr);
|
||||
@ -291,11 +266,9 @@ PipelineFactory::~PipelineFactory()
|
||||
{
|
||||
vkDestroyPipeline(device, basePipeline.instance, nullptr);
|
||||
vkDestroyPipelineLayout(device, basePipeline.layout, nullptr);
|
||||
vkDestroyRenderPass(device, basePipeline.renderPass, nullptr);
|
||||
for (Pipeline &pipeline : pipelines)
|
||||
for (auto &item : pipelines)
|
||||
{
|
||||
vkDestroyPipeline(device, pipeline.instance, nullptr);
|
||||
vkDestroyPipelineLayout(device, pipeline.layout, nullptr);
|
||||
vkDestroyRenderPass(device, pipeline.renderPass, nullptr);
|
||||
vkDestroyPipeline(device, item.second.instance, nullptr);
|
||||
vkDestroyPipelineLayout(device, item.second.layout, nullptr);
|
||||
}
|
||||
}
|
||||
@ -9,11 +9,20 @@
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
|
||||
Renderer::Renderer(SDL_Window *window, const AssetMaterial &baseMaterial) :
|
||||
bool operator < (const RenderObject& lhs, const RenderObject& rhs)
|
||||
{
|
||||
return &lhs.material < &rhs.material;
|
||||
}
|
||||
|
||||
Renderer::Renderer(GLFWwindow *window, const AssetMaterial &baseMaterial) :
|
||||
isRunning(false),
|
||||
baseMaterial(baseMaterial)
|
||||
{
|
||||
this->window = window;
|
||||
int width = -1, height = -1;
|
||||
glfwGetFramebufferSize(window, &width, &height);
|
||||
screenWidth.store(width);
|
||||
screenHeight.store(height);
|
||||
}
|
||||
|
||||
Renderer::~Renderer()
|
||||
@ -31,17 +40,10 @@ void Renderer::InitVulkan()
|
||||
};
|
||||
|
||||
// Get Vulkan extensions list
|
||||
uint32_t extensionsSDLCount = 0;
|
||||
if ( !SDL_Vulkan_GetInstanceExtensions(window, &extensionsSDLCount, nullptr) )
|
||||
LOG(CRITICAL, "Fail to get SDL Vulkan extensions number! %hs", SDL_GetError());
|
||||
|
||||
std::vector<const char*> vulkanExtensionsSDL(extensionsSDLCount);
|
||||
if ( !SDL_Vulkan_GetInstanceExtensions(window, &extensionsSDLCount, vulkanExtensionsSDL.data()) )
|
||||
{
|
||||
LOG(CRITICAL, "Fail to register SDL Vulkan extensions! %hs", SDL_GetError());
|
||||
vulkanExtensionsSDL.clear();
|
||||
}
|
||||
else vulkanExtensions.insert(vulkanExtensions.end(), vulkanExtensionsSDL.begin(), vulkanExtensionsSDL.end());
|
||||
uint32_t extensionsGLFWCount = 0;
|
||||
const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&extensionsGLFWCount);
|
||||
for (size_t i = 0; i < extensionsGLFWCount; i++)
|
||||
vulkanExtensions.push_back(glfwExtensions[i]);
|
||||
|
||||
for (const char *extension : vulkanExtensions)
|
||||
{
|
||||
@ -80,9 +82,11 @@ void Renderer::InitVulkan()
|
||||
else LOG(INFO, "Vulkan is instantiated");
|
||||
|
||||
// Create surface
|
||||
if ( !SDL_Vulkan_CreateSurface(window, vulkan, &surface) )
|
||||
LOG(CRITICAL, "Failed to create SDL Vulkan surface. %hs", SDL_GetError());
|
||||
else LOG(INFO, "SDL Vulkan surface created");
|
||||
|
||||
vkResult = glfwCreateWindowSurface(vulkan, window, nullptr, &surface);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(CRITICAL, "Failed to create GLFW Vulkan surface. Code: %d", vkResult);
|
||||
else LOG(INFO, "GLFW Vulkan surface created");
|
||||
}
|
||||
|
||||
void Renderer::SetupDevice()
|
||||
@ -230,10 +234,11 @@ void Renderer::SetupDevice()
|
||||
if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
|
||||
priority += GPU_DISCRETE_PRIORITY;
|
||||
|
||||
surfaceInfoBuffer.format.format = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
for (const VkSurfaceFormatKHR &format : surfaceInfoBuffer.availableFormats)
|
||||
{
|
||||
surfaceInfoBuffer.format = format;
|
||||
if (format.format == VK_FORMAT_B8G8R8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
||||
if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
||||
{
|
||||
priority += GPU_B8G8R8_SRGB_PRIORITY;
|
||||
break;
|
||||
@ -279,7 +284,7 @@ void Renderer::SetupDevice()
|
||||
.pNext = nullptr,
|
||||
.queueFamilyIndex = queueFamily,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &defaultQueuePriority
|
||||
.pQueuePriorities = &DEFAULT_QUEUE_PRIORITY
|
||||
};
|
||||
|
||||
queuesCreateConfigs.push_back(queueCreateConfig);
|
||||
@ -321,7 +326,7 @@ void Renderer::SetupSwapchain()
|
||||
LOG(CRITICAL, "Failed to get surface capabilities. Code: %d", vkResult);
|
||||
|
||||
int width = 0, height = 0;
|
||||
SDL_Vulkan_GetDrawableSize(window, &width, &height);
|
||||
glfwGetFramebufferSize(window, &width, &height);
|
||||
|
||||
// Clamp extent
|
||||
surfaceInfo.extent.width = (std::max)(
|
||||
@ -410,6 +415,313 @@ void Renderer::SetupSwapchain()
|
||||
LOG(INFO, "Frame views are ready.");
|
||||
}
|
||||
|
||||
void Renderer::RecreateSwapchain()
|
||||
{
|
||||
vkDeviceWaitIdle(device);
|
||||
ReleaseSwapchain();
|
||||
|
||||
SetupSwapchain();
|
||||
SetupRenderPasses();
|
||||
pipelineFactory = Allocate<PipelineFactory>(baseMaterial, baseRenderPass, device, surfaceInfo.extent);
|
||||
SetupBuffers();
|
||||
}
|
||||
|
||||
void Renderer::ReleaseSwapchain()
|
||||
{
|
||||
for (VkFramebuffer &frameBuffer : baseFrameBuffers)
|
||||
{
|
||||
vkDestroyFramebuffer(device, frameBuffer, nullptr);
|
||||
}
|
||||
vkFreeCommandBuffers(
|
||||
device,
|
||||
commandPool,
|
||||
static_cast<uint32_t>(commandBuffers.size()),
|
||||
commandBuffers.data()
|
||||
);
|
||||
|
||||
vkDestroyRenderPass(device, baseRenderPass, nullptr);
|
||||
Deallocate(pipelineFactory);
|
||||
|
||||
for (VkImageView &frameView : frameViews)
|
||||
{
|
||||
vkDestroyImageView(device, frameView, nullptr);
|
||||
}
|
||||
|
||||
vkDestroySwapchainKHR(device, swapchain, nullptr);
|
||||
}
|
||||
|
||||
void Renderer::SetupRenderPasses()
|
||||
{
|
||||
VkAttachmentDescription colorAttachmentConfig {
|
||||
.format = surfaceInfo.format.format,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT, // TODO: Multisampling
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
|
||||
};
|
||||
|
||||
VkAttachmentReference colorAttachmentRef {
|
||||
.attachment = 0,
|
||||
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
};
|
||||
|
||||
VkSubpassDescription subpass {
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &colorAttachmentRef
|
||||
};
|
||||
|
||||
VkSubpassDependency dependency
|
||||
{
|
||||
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
||||
.dstSubpass = 0,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
};
|
||||
|
||||
VkRenderPassCreateInfo baseRenderPassConfig {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &colorAttachmentConfig,
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpass,
|
||||
.dependencyCount = 1,
|
||||
.pDependencies = &dependency
|
||||
};
|
||||
|
||||
|
||||
VkResult vkResult = vkCreateRenderPass(device, &baseRenderPassConfig, nullptr, &baseRenderPass);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(CRITICAL, "Failed to create base render pass. Code: %d", vkResult);
|
||||
}
|
||||
|
||||
void Renderer::SetupBuffers()
|
||||
{
|
||||
VkResult vkResult;
|
||||
|
||||
baseFrameBuffers.resize(frameViews.size());
|
||||
size_t i = 0;
|
||||
for (const VkImageView &frameView : frameViews)
|
||||
{
|
||||
VkImageView attachments[] = {frameView};
|
||||
|
||||
VkFramebufferCreateInfo frameBufferConfig {
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.renderPass = baseRenderPass,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = attachments,
|
||||
.width = surfaceInfo.extent.width,
|
||||
.height = surfaceInfo.extent.height,
|
||||
.layers = 1
|
||||
};
|
||||
|
||||
vkResult = vkCreateFramebuffer(device, &frameBufferConfig, nullptr, &baseFrameBuffers[i]);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(CRITICAL, "Failed to create framebuffer for base render pass. Code: %d", vkResult);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (commandPool == nullptr)
|
||||
{
|
||||
VkCommandPoolCreateInfo commandPoolConfig {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
.queueFamilyIndex = queueFamilies.graphics,
|
||||
};
|
||||
|
||||
vkResult = vkCreateCommandPool(device, &commandPoolConfig, nullptr, &commandPool);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(CRITICAL, "Failed to create command pool. Code: %d", vkResult);
|
||||
}
|
||||
|
||||
commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
VkCommandBufferAllocateInfo allocateConfig {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.commandPool = commandPool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, //TODO: reuse some commands using SECONDARY
|
||||
.commandBufferCount = static_cast<uint32_t>(commandBuffers.size())
|
||||
};
|
||||
vkResult = vkAllocateCommandBuffers(device, &allocateConfig, commandBuffers.data());
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(CRITICAL, "Failed to allocate command buffers. Code: %d", vkResult);
|
||||
}
|
||||
|
||||
void Renderer::Draw()
|
||||
{
|
||||
VkResult vkResult;
|
||||
uint32_t imageIndex = 0;
|
||||
|
||||
vkResult = vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(ERROR, "Failed to wait for fence. Code: %d", vkResult);
|
||||
|
||||
vkResult = vkAcquireNextImageKHR(
|
||||
device, swapchain,
|
||||
UINT64_MAX,
|
||||
imageAvailable[currentFrame],
|
||||
VK_NULL_HANDLE,
|
||||
&imageIndex
|
||||
);
|
||||
switch (vkResult)
|
||||
{
|
||||
case VK_SUCCESS:
|
||||
break;
|
||||
|
||||
case VK_ERROR_OUT_OF_DATE_KHR:
|
||||
case VK_SUBOPTIMAL_KHR:
|
||||
LOG(WARNING, "Swapchain needs to be recreated");
|
||||
RecreateSwapchain();
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG(ERROR, "Failed to get next image index. Code: %d", vkResult);
|
||||
break;
|
||||
}
|
||||
|
||||
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE)
|
||||
{
|
||||
vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
|
||||
}
|
||||
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
|
||||
|
||||
vkResult = vkResetCommandBuffer(commandBuffers[currentFrame], 0);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(ERROR, "Failed to reset command buffer. Code: %d", vkResult);
|
||||
vkResult = vkBeginCommandBuffer(commandBuffers[currentFrame], &drawConfig.commandBeginConfig);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(ERROR, "Failed to begin command buffer. Code: %d", vkResult);
|
||||
|
||||
drawConfig.renderPassBeginConfig.renderPass = baseRenderPass;
|
||||
drawConfig.renderPassBeginConfig.framebuffer = baseFrameBuffers[imageIndex];
|
||||
drawConfig.renderPassBeginConfig.renderArea = {
|
||||
.offset = {.x = 0, .y = 0},
|
||||
.extent = surfaceInfo.extent
|
||||
};
|
||||
|
||||
vkCmdBeginRenderPass(
|
||||
commandBuffers[currentFrame],
|
||||
&drawConfig.renderPassBeginConfig,
|
||||
VK_SUBPASS_CONTENTS_INLINE
|
||||
);
|
||||
|
||||
Pipeline* lastPipeline = nullptr;
|
||||
Pipeline* pipeline = nullptr;
|
||||
for (const RenderObject &object : objects)
|
||||
{
|
||||
pipeline = &pipelineFactory->Get(object.material);
|
||||
if (pipeline != lastPipeline)
|
||||
{
|
||||
vkCmdBindPipeline(
|
||||
commandBuffers[currentFrame],
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
pipeline->instance
|
||||
);
|
||||
vkCmdSetViewport(commandBuffers[currentFrame], 0, 1, &pipeline->viewport);
|
||||
vkCmdSetScissor(commandBuffers[currentFrame], 0, 1, &pipeline->scissors);
|
||||
lastPipeline = pipeline;
|
||||
}
|
||||
|
||||
vkCmdDraw(
|
||||
commandBuffers[currentFrame],
|
||||
object.mesh.vertexCount,
|
||||
static_cast<uint32_t>(object.transforms.size()),
|
||||
0, // TODO: batching
|
||||
0 // TODO: batching
|
||||
);
|
||||
}
|
||||
|
||||
vkCmdEndRenderPass(commandBuffers[currentFrame]);
|
||||
vkResult = vkEndCommandBuffer(commandBuffers[currentFrame]);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(ERROR, "Failed to end command buffer. Code: %d", vkResult);
|
||||
|
||||
VkSemaphore waitSemaphores[] = {imageAvailable[currentFrame]};
|
||||
VkSemaphore signalSemaphores[] = {renderFinished[currentFrame]};
|
||||
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
||||
//drawConfig.submitConfig.pWaitSemaphores = &imageAvailable[currentFrame];
|
||||
drawConfig.submitConfig.pWaitSemaphores = waitSemaphores;
|
||||
//drawConfig.submitConfig.pSignalSemaphores = &renderFinished[currentFrame];
|
||||
drawConfig.submitConfig.pSignalSemaphores =signalSemaphores;
|
||||
drawConfig.submitConfig.pCommandBuffers = &commandBuffers[currentFrame];
|
||||
drawConfig.submitConfig.pWaitDstStageMask = waitStages;
|
||||
|
||||
vkResult = vkResetFences(device, 1, &inFlightFences[currentFrame]);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(ERROR, "Failed to reset for fence. Code: %d", vkResult);
|
||||
|
||||
vkResult = vkQueueSubmit(queues.graphics, 1, &drawConfig.submitConfig, inFlightFences[currentFrame]);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(ERROR, "Failed to submit render commands. Code: %d", vkResult);
|
||||
|
||||
//drawConfig.presentConfig.pWaitSemaphores = &renderFinished[currentFrame];
|
||||
drawConfig.presentConfig.pWaitSemaphores = signalSemaphores;
|
||||
VkSwapchainKHR swapChains[] = {swapchain};
|
||||
//drawConfig.presentConfig.pSwapchains = &swapchain;
|
||||
drawConfig.presentConfig.pSwapchains = swapChains;
|
||||
drawConfig.presentConfig.pImageIndices = &imageIndex;
|
||||
vkResult = vkQueuePresentKHR(queues.present, &drawConfig.presentConfig);
|
||||
switch (vkResult)
|
||||
{
|
||||
case VK_SUCCESS:
|
||||
break;
|
||||
|
||||
case VK_ERROR_OUT_OF_DATE_KHR:
|
||||
case VK_SUBOPTIMAL_KHR:
|
||||
LOG(WARNING, "Swapchain needs to be recreated");
|
||||
RecreateSwapchain();
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG(ERROR, "Failed to present swapchain. Code: %d", vkResult);
|
||||
break;
|
||||
}
|
||||
|
||||
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||
}
|
||||
|
||||
void Renderer::SetupSync()
|
||||
{
|
||||
VkResult vkResult;
|
||||
|
||||
VkSemaphoreCreateInfo semaphoreConfig {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
.pNext = nullptr
|
||||
};
|
||||
VkFenceCreateInfo fenceConfig {
|
||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = VK_FENCE_CREATE_SIGNALED_BIT
|
||||
};
|
||||
|
||||
imageAvailable.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
renderFinished.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
imagesInFlight.resize(frameViews.size(), VK_NULL_HANDLE);
|
||||
|
||||
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||
{
|
||||
vkResult = vkCreateSemaphore(device, &semaphoreConfig, nullptr, &imageAvailable[i]);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(CRITICAL, "Failed to create semaphore. Code: %d", vkResult);
|
||||
vkResult = vkCreateSemaphore(device, &semaphoreConfig, nullptr, &renderFinished[i]);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(CRITICAL, "Failed to create semaphore. Code: %d", vkResult);
|
||||
vkResult = vkCreateFence(device, &fenceConfig, nullptr, &inFlightFences[i]);
|
||||
if (vkResult != VK_SUCCESS)
|
||||
LOG(CRITICAL, "Failed to create fence. Code: %d", vkResult);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::Start()
|
||||
{
|
||||
if (isRunning)
|
||||
@ -430,14 +742,22 @@ void Renderer::Threading()
|
||||
Logger::trace->Register_Thread(L"Renderer", 0);
|
||||
LOG(INFO, "Renderer thread started");
|
||||
|
||||
Init();
|
||||
Loop();
|
||||
Release();
|
||||
}
|
||||
|
||||
void Renderer::Init()
|
||||
{
|
||||
InitVulkan();
|
||||
SetupDevice();
|
||||
SetupSwapchain();
|
||||
pipelineFactory = Allocate<PipelineFactory>(baseMaterial, device, surfaceInfo.extent, surfaceInfo.format.format);
|
||||
SetupRenderPasses();
|
||||
|
||||
Loop();
|
||||
pipelineFactory = Allocate<PipelineFactory>(baseMaterial, baseRenderPass, device, surfaceInfo.extent);
|
||||
|
||||
Release();
|
||||
SetupBuffers();
|
||||
SetupSync();
|
||||
}
|
||||
|
||||
void Renderer::Loop()
|
||||
@ -450,8 +770,9 @@ void Renderer::Loop()
|
||||
ProcessCommand(commandBuffer);
|
||||
}
|
||||
|
||||
|
||||
Draw();
|
||||
}
|
||||
vkDeviceWaitIdle(device);
|
||||
}
|
||||
|
||||
void Renderer::ProcessCommand(const RenderCommand &command)
|
||||
@ -465,6 +786,18 @@ void Renderer::ProcessCommand(const RenderCommand &command)
|
||||
break;
|
||||
}
|
||||
|
||||
case RenderCommand::REGISTER_OBJECTS:
|
||||
{
|
||||
LOG(INFO, "REGISTER_OBJECTS command");
|
||||
|
||||
for (size_t i = 0; i < command.objectsSize; i++)
|
||||
{
|
||||
objects.insert(command.objects[i]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case RenderCommand::RESIZE_WINDOW:
|
||||
{
|
||||
LOG(INFO, "RESIZE_WINDOW command");
|
||||
@ -494,16 +827,38 @@ void Renderer::Request(const RenderCommand::Type &type)
|
||||
commands.push(RenderCommand {.type = type});
|
||||
}
|
||||
|
||||
void Renderer::RegisterObjects(RenderObject* objects, size_t size)
|
||||
{
|
||||
RenderCommand command {
|
||||
.type = RenderCommand::REGISTER_OBJECTS,
|
||||
.objects = objects,
|
||||
.objectsSize = size
|
||||
};
|
||||
Request(command);
|
||||
}
|
||||
|
||||
void Renderer::ResizeWindow(int width, int height)
|
||||
{
|
||||
screenWidth.store(width);
|
||||
screenHeight.store(height);
|
||||
Request(RenderCommand::RESIZE_WINDOW);
|
||||
}
|
||||
|
||||
void Renderer::Release()
|
||||
{
|
||||
Deallocate(pipelineFactory);
|
||||
vkDeviceWaitIdle(device);
|
||||
|
||||
for (VkImageView &frameView : frameViews)
|
||||
ReleaseSwapchain();
|
||||
|
||||
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||
{
|
||||
vkDestroyImageView(device, frameView, nullptr);
|
||||
vkDestroySemaphore(device, renderFinished[i], nullptr);
|
||||
vkDestroySemaphore(device, imageAvailable[i], nullptr);
|
||||
vkDestroyFence(device, inFlightFences[i], nullptr);
|
||||
}
|
||||
|
||||
vkDestroySwapchainKHR(device, swapchain, nullptr);
|
||||
vkDestroyCommandPool(device, commandPool, nullptr);
|
||||
|
||||
vkDestroyDevice(device, nullptr);
|
||||
vkDestroySurfaceKHR(vulkan, surface, nullptr);
|
||||
vkDestroyInstance(vulkan, nullptr);
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
#include "Logger.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "compile_config.hpp"
|
||||
#include "Engine.hpp"
|
||||
|
||||
IP7_Trace* Logger::trace;
|
||||
|
||||
int SDL_main(int argc, char **argv)
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Setup P7 logger
|
||||
IP7_Client *p7client = P7_Create_Client(P7_SINK);
|
||||
@ -16,28 +16,15 @@ int SDL_main(int argc, char **argv)
|
||||
|
||||
LOG(INFO, "Etredia Empires, v%d.%d.%d", MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION);
|
||||
|
||||
// Setup SDL
|
||||
bool quit = false;
|
||||
SDL_Event event;
|
||||
// Setup GLFW
|
||||
glfwInit();
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER);
|
||||
|
||||
SDL_Window *window = SDL_CreateWindow(
|
||||
"Etredia Empires",
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED,
|
||||
800,
|
||||
600,
|
||||
SDL_WINDOW_VULKAN
|
||||
);
|
||||
SDL_StopTextInput();
|
||||
GLFWwindow *window = glfwCreateWindow(800, 600, "Etredia Empires", nullptr, nullptr);
|
||||
|
||||
if (!window)
|
||||
{
|
||||
quit = true;
|
||||
LOG(CRITICAL, "SDL Window fails: %hs", SDL_GetError());
|
||||
}
|
||||
else LOG(INFO, "SDL Window created");
|
||||
LOG(CRITICAL, "GLFW Window failed");
|
||||
else LOG(INFO, "GLFW Window created");
|
||||
|
||||
// Setup assets
|
||||
AssetManager assetManager;
|
||||
@ -47,51 +34,35 @@ int SDL_main(int argc, char **argv)
|
||||
// Setup Renderer
|
||||
assetManager.LoadAsync(AssetInfo::MATERIAL, "assets/materials/helloworld.mat");
|
||||
AssetHandle baseMaterialHandle = assetManager.Find("assets/materials/helloworld.mat");
|
||||
Renderer renderer(window, assetManager.Get<AssetMaterial>(baseMaterialHandle));
|
||||
AssetMaterial baseMaterial = assetManager.Get<AssetMaterial>(baseMaterialHandle);
|
||||
Renderer renderer(window, baseMaterial);
|
||||
renderer.Start();
|
||||
|
||||
//Setup Engine
|
||||
Engine::assetManager = &assetManager;
|
||||
Engine::renderer = &renderer;
|
||||
|
||||
// Process SDL events
|
||||
while(!quit)
|
||||
// Test render obj
|
||||
RenderObject triangle {
|
||||
.transforms = {{.x = 0, .y = 0}},
|
||||
.mesh = {.vertexCount = 3},
|
||||
|
||||
.material = baseMaterial,
|
||||
};
|
||||
renderer.RegisterObjects(&triangle, 1);
|
||||
|
||||
// Process GLFW events
|
||||
while(!glfwWindowShouldClose(window))
|
||||
{
|
||||
SDL_PollEvent(&event);
|
||||
|
||||
switch(event.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
{
|
||||
SDL_RestoreWindow(window);
|
||||
quit = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
{
|
||||
switch (event.window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
{
|
||||
renderer.screenWidth.store(event.window.data1);
|
||||
renderer.screenHeight.store(event.window.data2);
|
||||
renderer.Request(RenderCommand::RESIZE_WINDOW);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
// Free memory
|
||||
LOG(INFO, "Exit...");
|
||||
renderer.Shutdown(); while(renderer.isRunning.load());
|
||||
assetManager.Shutdown(); while(assetManager.isRunning.load());
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
glfwDestroyWindow(window);
|
||||
glfwTerminate();
|
||||
Logger::trace->Unregister_Thread(0);
|
||||
Logger::trace->Release();
|
||||
p7client->Release();
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
{
|
||||
"vertexShader": "assets/shaders/helloworld.vert.spv",
|
||||
"fragmentShader": "assets/shaders/helloworld.frag.spv"
|
||||
}
|
||||
@ -8,7 +8,13 @@
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AssetMaterial, vertexShader, fragmentShader)
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
|
||||
AssetMaterial,
|
||||
vertexShaderAssetID,
|
||||
fragmentShaderAssetID,
|
||||
renderType,
|
||||
renderStage
|
||||
)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
@ -32,15 +38,17 @@ int main(int argc, char **argv)
|
||||
|
||||
json data = json::parse(fin);
|
||||
|
||||
for (const char *link : {
|
||||
"vertexShader",
|
||||
"fragmentShader"
|
||||
})
|
||||
for (auto &[key, value] : data.items())
|
||||
{
|
||||
std::filesystem::path linkPath = data[link];
|
||||
if (
|
||||
!key.ends_with("AssetID") &&
|
||||
!key.ends_with("Type")
|
||||
) continue;
|
||||
|
||||
std::filesystem::path linkPath = data[key];
|
||||
auto linkPath8 = linkPath.generic_u8string();
|
||||
uint32_t assetId = XXH32(linkPath8.data(), linkPath8.size(), 0);
|
||||
data[link] = assetId;
|
||||
data[key] = assetId;
|
||||
}
|
||||
|
||||
AssetMaterial material = data.get<AssetMaterial>();
|
||||
6
sdk/materials/helloworld.mat.json
Normal file
6
sdk/materials/helloworld.mat.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"vertexShaderAssetID": "assets/shaders/helloworld.vert.spv",
|
||||
"fragmentShaderAssetID": "assets/shaders/helloworld.frag.spv",
|
||||
"renderType": "base",
|
||||
"renderStage": 0
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
#version 330
|
||||
#version 450
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
@ -1,4 +1,4 @@
|
||||
#version 330
|
||||
#version 450
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
@ -5,7 +5,7 @@
|
||||
"nlohmann-json",
|
||||
"entt",
|
||||
"vulkan",
|
||||
{"name": "sdl2", "features": ["vulkan"]},
|
||||
"glfw3",
|
||||
"rmlui",
|
||||
"gtest"
|
||||
]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user