diff --git a/include/application.hpp b/include/application.hpp index 46c83ed..2611171 100644 --- a/include/application.hpp +++ b/include/application.hpp @@ -4,6 +4,8 @@ #include "gfx.hpp" +#include + #include #include #include @@ -22,6 +24,19 @@ namespace engine { class Texture; } + struct RenderData { + std::unique_ptr gfxdev; + gfx::DrawBuffer* drawBuffer = nullptr; + /* uniforms for engine globals */ + const gfx::DescriptorSetLayout* setZeroLayout; + const gfx::DescriptorSet* setZero; + struct SetZeroBuffer { + glm::mat4 proj; + glm::vec2 myValue; + }; + gfx::DescriptorBuffer* setZeroBuffer; + }; + class Application { public: @@ -61,17 +76,16 @@ namespace engine { /* getters */ Window* window() { return m_window.get(); } - GFXDevice* gfx() { return m_gfx.get(); } + GFXDevice* gfx() { return renderData.gfxdev.get(); } InputManager* inputManager() { return m_inputManager.get(); } SceneManager* sceneManager() { return m_sceneManager.get(); } - gfx::DrawBuffer* getDrawBuffer() { return m_drawBuffer; } - std::string getResourcePath(const std::string relativePath) { return (m_resourcesPath / relativePath).string(); } + RenderData renderData{}; + private: std::unique_ptr m_window; - std::unique_ptr m_gfx; std::unique_ptr m_inputManager; std::unique_ptr m_sceneManager; @@ -79,8 +93,6 @@ namespace engine { bool m_enableFrameLimiter = true; - gfx::DrawBuffer *m_drawBuffer; - /* resource stuff */ std::unordered_map> m_resourceManagers{}; diff --git a/include/gfx.hpp b/include/gfx.hpp index f0ac2fb..1136c0f 100644 --- a/include/gfx.hpp +++ b/include/gfx.hpp @@ -9,6 +9,7 @@ namespace engine::gfx { // handles (incomplete types) struct Pipeline; + struct DescriptorBuffer; struct Buffer; struct Texture; struct DrawBuffer; diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index a7d4bd9..c368974 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -26,9 +26,9 @@ namespace engine { void cmdBindVertexBuffer(gfx::DrawBuffer* drawBuffer, uint32_t binding, const gfx::Buffer* buffer); void cmdBindIndexBuffer(gfx::DrawBuffer* drawBuffer, const gfx::Buffer* buffer); void cmdDrawIndexed(gfx::DrawBuffer* drawBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance); + void cmdPushConstants(gfx::DrawBuffer* drawBuffer, const gfx::Pipeline* pipeline, uint32_t offset, uint32_t size, const void* data); void cmdBindDescriptorSet(gfx::DrawBuffer* drawBuffer, const gfx::Pipeline* pipeline, const gfx::DescriptorSet* set, uint32_t setNumber); - // creates the equivalent of an OpenGL shader program & vertex attrib configuration gfx::Pipeline* createPipeline(const gfx::PipelineInfo& info); void destroyPipeline(const gfx::Pipeline* pipeline); @@ -36,11 +36,15 @@ namespace engine { void destroyDescriptorSetLayout(const gfx::DescriptorSetLayout* layout); gfx::DescriptorSet* allocateDescriptorSet(const gfx::DescriptorSetLayout* layout); - void updateDescriptor(const gfx::DescriptorSet* set, uint32_t binding, const gfx::Buffer* buffer); + // This updates all copies of the descriptor. This cannot be used after any frames have been renderered + void updateDescriptor(const gfx::DescriptorSet* set, uint32_t binding, const gfx::DescriptorBuffer* buffer, size_t offset, size_t range); - void updateUniformBuffer(const gfx::Pipeline* pipeline, const void* data, size_t size, uint32_t offset); + gfx::DescriptorBuffer* createDescriptorBuffer(uint64_t size, const void* initialData); + void destroyDescriptorBuffer(const gfx::DescriptorBuffer* descriptorBuffer); - // Tries to create it on the GPU. Cannot be directly updated by the CPU. + void writeDescriptorBuffer(gfx::DescriptorBuffer* buffer, uint64_t offset, uint64_t size, const void* data); + + // Loads data into staging buffer and copies that into a single GPU buffer. gfx::Buffer* createBuffer(gfx::BufferType type, uint64_t size, const void* data); void destroyBuffer(const gfx::Buffer* buffer); @@ -54,6 +58,8 @@ namespace engine { bool useAnisotropy = false); void destroyTexture(const gfx::Texture* texture); + uint64_t getFrameCount(); + // wait until all the active GPU queues have finished working void waitIdle(); diff --git a/include/resources/shader.hpp b/include/resources/shader.hpp index 1ef8671..6850dbb 100644 --- a/include/resources/shader.hpp +++ b/include/resources/shader.hpp @@ -4,6 +4,7 @@ namespace engine { class GFXDevice; + struct RenderData; } namespace engine::resources { @@ -20,20 +21,15 @@ public: bool hasUV0; // vec2 }; - Shader(GFXDevice* gfx, const char* vertPath, const char* fragPath, const VertexParams& vertexParams, bool alphaBlending, bool cullBackFace); + Shader(RenderData* renderData, const char* vertPath, const char* fragPath, const VertexParams& vertexParams, bool alphaBlending, bool cullBackFace); ~Shader(); Shader(const Shader&) = delete; Shader& operator=(const Shader&) = delete; const gfx::Pipeline* getPipeline(); - const gfx::DescriptorSet* getSetZero() { return m_setZero; } - private: GFXDevice* const m_gfx; - const gfx::DescriptorSetLayout* m_setZeroLayout; - const gfx::DescriptorSet* m_setZero; - const gfx::Buffer* m_setZeroBuffer; const gfx::Pipeline* m_pipeline; }; diff --git a/include/systems/render.hpp b/include/systems/render.hpp index 8c3e1cc..ecd8778 100644 --- a/include/systems/render.hpp +++ b/include/systems/render.hpp @@ -7,18 +7,24 @@ #include "components/transform.hpp" #include "components/renderable.hpp" +#include "gfx.hpp" +#include "gfx_device.hpp" + namespace engine { class RenderSystem : public System { public: RenderSystem(Scene* scene); + ~RenderSystem(); void onUpdate(float ts) override; void setCameraEntity(uint32_t entity); private: + GFXDevice* const m_gfx; + struct { // only uses transform component, which is required for all entities anyway uint32_t camEntity = 0; @@ -29,6 +35,8 @@ namespace engine { float m_viewportAspectRatio = 1.0f; + float m_value = 0.0f; + }; } diff --git a/res/engine/shaders/showNormals.vert b/res/engine/shaders/showNormals.vert index 5bdcbd7..40542ae 100644 --- a/res/engine/shaders/showNormals.vert +++ b/res/engine/shaders/showNormals.vert @@ -1,14 +1,15 @@ #version 450 -layout(binding = 0) uniform UBO { - mat4 proj; -} ubo; - layout( push_constant ) uniform Constants { mat4 model; mat4 view; } constants; +layout(set = 0, binding = 0) uniform SetZeroBuffer { + mat4 proj; + vec2 myValue; +} setZeroBuffer; + layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inNorm; layout(location = 2) in vec2 inUV; @@ -16,6 +17,6 @@ layout(location = 2) in vec2 inUV; layout(location = 0) out vec3 fragNorm; void main() { - gl_Position = ubo.proj * constants.view * constants.model * vec4(inPosition, 1.0); + gl_Position = setZeroBuffer.proj * constants.view * constants.model * vec4(inPosition, 1.0); fragNorm = mat3(transpose(inverse(constants.model))) * inNorm; } diff --git a/res/engine/shaders/showUVs.vert b/res/engine/shaders/showUVs.vert index c4bc3ec..429bda6 100644 --- a/res/engine/shaders/showUVs.vert +++ b/res/engine/shaders/showUVs.vert @@ -1,14 +1,15 @@ #version 450 -layout(binding = 0) uniform UBO { - mat4 proj; -} ubo; - layout( push_constant ) uniform Constants { mat4 model; mat4 view; } constants; +layout(set = 0, binding = 0) uniform SetZeroBuffer { + mat4 proj; + vec2 myValue; +} setZeroBuffer; + layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inNorm; layout(location = 2) in vec2 inUV; @@ -16,6 +17,6 @@ layout(location = 2) in vec2 inUV; layout(location = 0) out vec2 fragUV; void main() { - gl_Position = ubo.proj * constants.view * constants.model * vec4(inPosition, 1.0); + gl_Position = setZeroBuffer.proj * constants.view * constants.model * vec4(inPosition, 1.0); fragUV = inUV; } diff --git a/res/engine/shaders/skybox.frag b/res/engine/shaders/skybox.frag index a582a9a..4661915 100644 --- a/res/engine/shaders/skybox.frag +++ b/res/engine/shaders/skybox.frag @@ -4,12 +4,13 @@ layout(location = 0) in vec2 fragUV; layout(location = 0) out vec4 outColor; -layout(set = 1, binding = 0) uniform sampler2D texSampler; +//layout(set = 1, binding = 0) uniform sampler2D texSampler; void main() { gl_FragDepth = 0.9999; - outColor = texture(texSampler, fragUV); + //outColor = texture(texSampler, fragUV); + outColor = vec4(0.1, 0.1, 0.1, 1.0); } diff --git a/res/engine/shaders/skybox.vert b/res/engine/shaders/skybox.vert index c7d5bf9..b3a20cd 100644 --- a/res/engine/shaders/skybox.vert +++ b/res/engine/shaders/skybox.vert @@ -1,15 +1,15 @@ #version 450 -layout(binding = 0) uniform UBO { - mat4 proj; - mat4 view; -} ubo; - layout( push_constant ) uniform Constants { mat4 model; mat4 view; } constants; +layout(set = 0, binding = 0) uniform SetZeroBuffer { + mat4 proj; + vec2 myValue; +} setZeroBuffer; + layout(location = 0) in vec3 inPosition; layout(location = 2) in vec2 inUV; @@ -18,7 +18,7 @@ layout(location = 0) out vec2 fragUV; void main() { mat4 myView = constants.view; myView[3] = vec4(0.0, 0.0, 0.0, 1.0); - vec4 pos = ubo.proj * myView * constants.model * vec4(inPosition, 1.0); + vec4 pos = setZeroBuffer.proj * myView * constants.model * vec4(inPosition, 1.0); gl_Position = pos; fragUV = inUV; } diff --git a/res/engine/shaders/standard.frag b/res/engine/shaders/standard.frag index 1fdde95..91f931a 100644 --- a/res/engine/shaders/standard.frag +++ b/res/engine/shaders/standard.frag @@ -7,7 +7,7 @@ layout(location = 3) in vec3 fragLightPos; layout(location = 0) out vec4 outColor; -layout(set = 1, binding = 0) uniform sampler2D texSampler; +//layout(set = 1, binding = 0) uniform sampler2D texSampler; void main() { @@ -15,7 +15,8 @@ void main() { vec3 lightColor = vec3(1.0, 1.0, 1.0); vec3 ambientColor = vec3(1.0, 1.0, 1.0); float ambientStrength = 0.05; - vec3 baseColor = vec3(texture(texSampler, fragUV)); + //vec3 baseColor = vec3(texture(texSampler, fragUV)); + vec3 baseColor = vec3(mod(fragUV.x, 1.0), mod(fragUV.y, 1.0), 0.0); vec3 emission = vec3(0.0, 0.0, 0.0); // code diff --git a/res/engine/shaders/standard.vert b/res/engine/shaders/standard.vert index 8943857..6bd0aed 100644 --- a/res/engine/shaders/standard.vert +++ b/res/engine/shaders/standard.vert @@ -1,15 +1,15 @@ #version 450 -layout(set = 0, binding = 0) uniform UBO { - mat4 proj; - mat4 view; -} ubo; - layout( push_constant ) uniform Constants { mat4 model; mat4 view; } constants; +layout(set = 0, binding = 0) uniform SetZeroBuffer { + mat4 proj; + vec2 myValue; +} setZeroBuffer; + layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inNorm; layout(location = 2) in vec2 inUV; @@ -20,7 +20,7 @@ layout(location = 2) out vec2 fragUV; layout(location = 3) out vec3 fragLightPos; void main() { - gl_Position = ubo.proj * constants.view * constants.model * vec4(inPosition, 1.0); + gl_Position = setZeroBuffer.proj * constants.view * constants.model * vec4(inPosition, 1.0); fragPos = vec3(constants.view * constants.model * vec4(inPosition, 1.0)); fragNorm = mat3(transpose(inverse(constants.view * constants.model))) * inNorm; diff --git a/res/engine/shaders/test.frag b/res/engine/shaders/test.frag deleted file mode 100644 index 05d548c..0000000 --- a/res/engine/shaders/test.frag +++ /dev/null @@ -1,12 +0,0 @@ -#version 450 - -layout(location = 0) in vec2 fragUV; - -layout(location = 0) out vec4 outColor; - -void main() -{ - vec3 baseColor = vec3(fragUV, 0.0); - outColor = vec4(baseColor, 1.0); -} - diff --git a/res/engine/shaders/test.vert b/res/engine/shaders/test.vert deleted file mode 100644 index 0201736..0000000 --- a/res/engine/shaders/test.vert +++ /dev/null @@ -1,16 +0,0 @@ -#version 450 - -layout(location = 0) in vec3 inPosition; -layout(location = 2) in vec2 inUV; - -layout(location = 0) out vec2 fragUV; - -layout(set = 0, binding = 0) uniform MyUniform { - vec4 someValue; -} myUniform; - -void main() -{ - gl_Position = vec4(inPosition, 1.0); - fragUV = inUV * myUniform.someValue.xy; -} diff --git a/src/application.cpp b/src/application.cpp index d250fda..d6fb716 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -14,6 +14,8 @@ #include "resources/shader.hpp" #include "resources/texture.hpp" +#include + // To allow the FPS-limiter to put the thread to sleep #include @@ -23,43 +25,42 @@ #define MAX_PATH 260 #endif -static std::filesystem::path getResourcesPath() -{ - std::filesystem::path resourcesPath{}; +namespace engine { + + static std::filesystem::path getResourcesPath() + { + std::filesystem::path resourcesPath{}; #ifdef _MSC_VER - CHAR exeDirBuf[MAX_PATH + 1]; - GetModuleFileNameA(NULL, exeDirBuf, MAX_PATH + 1); - std::filesystem::path cwd = std::filesystem::path(exeDirBuf).parent_path(); - (void)_chdir((const char*)std::filesystem::absolute(cwd).c_str()); + CHAR exeDirBuf[MAX_PATH + 1]; + GetModuleFileNameA(NULL, exeDirBuf, MAX_PATH + 1); + std::filesystem::path cwd = std::filesystem::path(exeDirBuf).parent_path(); + (void)_chdir((const char*)std::filesystem::absolute(cwd).c_str()); #else - std::filesystem::path cwd = std::filesystem::current_path(); + std::filesystem::path cwd = std::filesystem::current_path(); #endif - if (std::filesystem::is_directory(cwd / "res")) { - resourcesPath = cwd / "res"; - } - else { - resourcesPath = cwd.parent_path() / "share" / "sdltest"; - } + if (std::filesystem::is_directory(cwd / "res")) { + resourcesPath = cwd / "res"; + } + else { + resourcesPath = cwd.parent_path() / "share" / "sdltest"; + } - if (std::filesystem::is_directory(resourcesPath) == false) { - resourcesPath = cwd.root_path() / "usr" / "local" / "share" / "sdltest"; + if (std::filesystem::is_directory(resourcesPath) == false) { + resourcesPath = cwd.root_path() / "usr" / "local" / "share" / "sdltest"; + } + + if (std::filesystem::is_directory(resourcesPath) == false) { + throw std::runtime_error("Unable to determine resources location. CWD: " + cwd.string()); + } + + return resourcesPath; } - if (std::filesystem::is_directory(resourcesPath) == false) { - throw std::runtime_error("Unable to determine resources location. CWD: " + cwd.string()); - } - - return resourcesPath; -} - -namespace engine { - Application::Application(const char* appName, const char* appVersion, gfx::GraphicsSettings graphicsSettings) { m_window = std::make_unique(appName, true, false); - m_gfx = std::make_unique(appName, appVersion, m_window->getHandle(), graphicsSettings); m_inputManager = std::make_unique(window()); m_sceneManager = std::make_unique(this); @@ -72,14 +73,24 @@ namespace engine { registerResourceManager(); registerResourceManager(); + // initialise the render data + renderData.gfxdev = std::make_unique(appName, appVersion, m_window->getHandle(), graphicsSettings); + renderData.setZeroLayout = gfx()->createDescriptorSetLayout(); + renderData.setZero = gfx()->allocateDescriptorSet(renderData.setZeroLayout); + RenderData::SetZeroBuffer initialData{ + .proj = glm::perspectiveZO(glm::radians(70.0f), 1024.0f / 768.0f, 0.1f, 1000.0f), + .myValue = { 0.0f, 1.0f } + }; + renderData.setZeroBuffer = gfx()->createDescriptorBuffer(sizeof(RenderData::SetZeroBuffer), &initialData); + gfx()->updateDescriptor(renderData.setZero, 0, renderData.setZeroBuffer, 0, sizeof(RenderData::SetZeroBuffer)); + // default resources -#if 0 { resources::Shader::VertexParams vertParams{}; vertParams.hasNormal = true; vertParams.hasUV0 = true; auto texturedShader = std::make_unique( - gfx(), + &renderData, getResourcePath("engine/shaders/standard.vert").c_str(), getResourcePath("engine/shaders/standard.frag").c_str(), vertParams, @@ -93,7 +104,7 @@ namespace engine { vertParams.hasNormal = true; vertParams.hasUV0 = true; auto texturedShader = std::make_unique( - gfx(), + &renderData, getResourcePath("engine/shaders/skybox.vert").c_str(), getResourcePath("engine/shaders/skybox.frag").c_str(), vertParams, @@ -102,7 +113,6 @@ namespace engine { ); getResourceManager()->addPersistent("builtin.skybox", std::move(texturedShader)); } -#endif { auto whiteTexture = std::make_unique( gfx(), @@ -115,7 +125,11 @@ namespace engine { } } - Application::~Application() {} + Application::~Application() + { + gfx()->destroyDescriptorBuffer(renderData.setZeroBuffer); + gfx()->destroyDescriptorSetLayout(renderData.setZeroLayout); + } void Application::gameLoop() { @@ -132,7 +146,7 @@ namespace engine { while (m_window->isRunning()) { /* begin rendering */ - m_drawBuffer = m_gfx->beginRender(); + renderData.drawBuffer = gfx()->beginRender(); /* logic */ m_sceneManager->updateActiveScene(m_window->dt()); @@ -149,7 +163,7 @@ namespace engine { } /* draw */ - m_gfx->finishRender(m_drawBuffer); + gfx()->finishRender(renderData.drawBuffer); /* poll events */ m_window->getInputAndEvents(); @@ -163,7 +177,7 @@ namespace engine { } - m_gfx->waitIdle(); + gfx()->waitIdle(); } } diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index aefd00b..2da2adf 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -61,6 +61,7 @@ namespace engine { VkBuffer buffer = VK_NULL_HANDLE; VmaAllocation allocation = nullptr; VkDeviceSize size = 0; + bool hostVisible = false; }; struct gfx::Pipeline { @@ -80,6 +81,7 @@ namespace engine { struct gfx::DrawBuffer { FrameData frameData; + uint32_t currentFrameIndex; // corresponds to the frameData uint32_t imageIndex; // for swapchain present }; @@ -88,7 +90,12 @@ namespace engine { }; struct gfx::DescriptorSet { - VkDescriptorSet set; + std::array sets; // frames in flight cannot use the same descriptor set in case the buffer needs updating + }; + + struct gfx::DescriptorBuffer { + gfx::Buffer stagingBuffer{}; + std::array gpuBuffers; }; // enum converters @@ -552,6 +559,7 @@ namespace engine { Swapchain swapchain{}; VkDescriptorPool descriptorPool; + std::array, FRAMES_IN_FLIGHT> descriptorBufferWriteQueues{}; uint64_t FRAMECOUNT = 0; @@ -696,9 +704,17 @@ namespace engine { { VkResult res; - uint32_t swapchainImageIndex; + const uint32_t currentFrameIndex = pimpl->FRAMECOUNT % FRAMES_IN_FLIGHT; + const FrameData frameData = pimpl->frameData[currentFrameIndex]; - FrameData frameData = pimpl->frameData[pimpl->FRAMECOUNT % FRAMES_IN_FLIGHT]; + /* first empty the descriptor buffer write queue */ + auto& writeQueue = pimpl->descriptorBufferWriteQueues[currentFrameIndex]; + for (gfx::DescriptorBuffer* buffer : writeQueue) { + copyBuffer(pimpl->device.device, pimpl->device.commandPools.transfer, pimpl->device.queues.transferQueues[0], buffer->stagingBuffer.buffer, buffer->gpuBuffers[currentFrameIndex].buffer, buffer->stagingBuffer.size); + } + writeQueue.clear(); + + uint32_t swapchainImageIndex; do { if (pimpl->swapchainIsOutOfDate) { @@ -736,10 +752,10 @@ namespace engine { { // RECORDING - VkClearValue clearValue{}; + VkClearValue clearValue{}; // Using same value for all components enables compression according to NVIDIA Best Practices clearValue.color.float32[0] = 1.0f; - clearValue.color.float32[1] = 0.0f; - clearValue.color.float32[2] = 0.0f; + clearValue.color.float32[1] = 1.0f; + clearValue.color.float32[2] = 1.0f; clearValue.color.float32[3] = 1.0f; VkRenderPassBeginInfo passBegin{ @@ -773,6 +789,7 @@ namespace engine { // hand command buffer over to caller gfx::DrawBuffer* drawBuffer = new gfx::DrawBuffer; drawBuffer->frameData = frameData; + drawBuffer->currentFrameIndex = currentFrameIndex; drawBuffer->imageIndex = swapchainImageIndex; return drawBuffer; @@ -862,9 +879,15 @@ namespace engine { vkCmdDrawIndexed(drawBuffer->frameData.drawBuf, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); } + void GFXDevice::cmdPushConstants(gfx::DrawBuffer* drawBuffer, const gfx::Pipeline* pipeline, uint32_t offset, uint32_t size, const void* data) + { + assert(drawBuffer != nullptr); + vkCmdPushConstants(drawBuffer->frameData.drawBuf, pipeline->layout, VK_SHADER_STAGE_VERTEX_BIT, offset, size, data); + } + void GFXDevice::cmdBindDescriptorSet(gfx::DrawBuffer* drawBuffer, const gfx::Pipeline* pipeline, const gfx::DescriptorSet* set, uint32_t setNumber) { - vkCmdBindDescriptorSets(drawBuffer->frameData.drawBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->layout, setNumber, 1, &set->set, 0, nullptr); + vkCmdBindDescriptorSets(drawBuffer->frameData.drawBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->layout, setNumber, 1, &set->sets[drawBuffer->currentFrameIndex], 0, nullptr); } gfx::Pipeline* GFXDevice::createPipeline(const gfx::PipelineInfo& info) @@ -1114,61 +1137,133 @@ namespace engine { { gfx::DescriptorSet* set = new gfx::DescriptorSet{}; - VkDescriptorSetAllocateInfo allocInfo{ + for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) { + VkDescriptorSetAllocateInfo allocInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .pNext = nullptr, .descriptorPool = pimpl->descriptorPool, .descriptorSetCount = 1, .pSetLayouts = &layout->layout - }; - - VkResult res; - res = vkAllocateDescriptorSets(pimpl->device.device, &allocInfo, &set->set); - if (res == VK_ERROR_FRAGMENTED_POOL) throw std::runtime_error("Descriptor pool is fragmented!"); - if (res == VK_ERROR_OUT_OF_POOL_MEMORY) throw std::runtime_error("Descriptor pool is out of memory!"); - VKCHECK(res); + }; + VkResult res; + res = vkAllocateDescriptorSets(pimpl->device.device, &allocInfo, &set->sets[i]); + if (res == VK_ERROR_FRAGMENTED_POOL) throw std::runtime_error("Descriptor pool is fragmented!"); + if (res == VK_ERROR_OUT_OF_POOL_MEMORY) throw std::runtime_error("Descriptor pool is out of memory!"); + VKCHECK(res); + } return set; } - void GFXDevice::updateDescriptor(const gfx::DescriptorSet* set, uint32_t binding, const gfx::Buffer* buffer) + void GFXDevice::updateDescriptor(const gfx::DescriptorSet* set, uint32_t binding, const gfx::DescriptorBuffer* buffer, size_t offset, size_t range) { - VkDescriptorBufferInfo bufferInfo{ - .buffer = buffer->buffer, - .offset = 0, - .range = VK_WHOLE_SIZE - }; - VkWriteDescriptorSet descriptorWrite{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = set->set, - .dstBinding = binding, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .pImageInfo = nullptr, - .pBufferInfo = &bufferInfo, - .pTexelBufferView = nullptr - }; - vkUpdateDescriptorSets(pimpl->device.device, 1, &descriptorWrite, 0, nullptr); + assert(pimpl->FRAMECOUNT == 0); + + for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) { + VkDescriptorBufferInfo bufferInfo{ + .buffer = buffer->gpuBuffers[i].buffer, + .offset = offset, + .range = range + }; + VkWriteDescriptorSet descriptorWrite{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = set->sets[i], + .dstBinding = binding, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .pImageInfo = nullptr, + .pBufferInfo = &bufferInfo, + .pTexelBufferView = nullptr + }; + vkUpdateDescriptorSets(pimpl->device.device, 1, &descriptorWrite, 0, nullptr); + } } - void GFXDevice::updateUniformBuffer(const gfx::Pipeline* pipeline, const void* data, size_t size, uint32_t offset) + gfx::DescriptorBuffer* GFXDevice::createDescriptorBuffer(uint64_t size, const void* initialData) { + gfx::DescriptorBuffer* out = new gfx::DescriptorBuffer{}; + + /* first make staging buffer */ + out->stagingBuffer.size = size; + out->stagingBuffer.type = gfx::BufferType::UNIFORM; + out->stagingBuffer.hostVisible = true; + { + VkBufferCreateInfo stagingBufferInfo{}; + stagingBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + stagingBufferInfo.size = out->stagingBuffer.size; + stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + stagingBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + stagingBufferInfo.flags = 0; -#if 0 - assert(size <= pipeline->uniformBuffers[0]->size); + VmaAllocationCreateInfo stagingAllocInfo{}; + stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO; + stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + stagingAllocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - [[maybe_unused]] VkResult res; + VKCHECK(vmaCreateBuffer(pimpl->allocator, &stagingBufferInfo, &stagingAllocInfo, &out->stagingBuffer.buffer, &out->stagingBuffer.allocation, nullptr)); - for (gfx::Buffer* buffer : pipeline->uniformBuffers) { - void* uniformDest = nullptr; - res = vmaMapMemory(pimpl->allocator, buffer->allocation, &uniformDest); - assert(res == VK_SUCCESS); - memcpy((uint8_t*)uniformDest + offset, data, size); - vmaUnmapMemory(pimpl->allocator, buffer->allocation); + void* dataDest; + VKCHECK(vmaMapMemory(pimpl->allocator, out->stagingBuffer.allocation, &dataDest)); + memcpy(dataDest, initialData, out->stagingBuffer.size); + vmaUnmapMemory(pimpl->allocator, out->stagingBuffer.allocation); + } + + /* create the device-local set of buffers */ + for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) { + + out->gpuBuffers[i].size = out->stagingBuffer.size; + out->gpuBuffers[i].type = gfx::BufferType::UNIFORM; + out->gpuBuffers[i].hostVisible = false; + + VkBufferCreateInfo gpuBufferInfo{}; + gpuBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + gpuBufferInfo.size = out->gpuBuffers[i].size; + gpuBufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + gpuBufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + gpuBufferInfo.flags = 0; + + VmaAllocationCreateInfo gpuAllocationInfo{}; + gpuAllocationInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + gpuAllocationInfo.flags = 0; + + VKCHECK(vmaCreateBuffer(pimpl->allocator, &gpuBufferInfo, &gpuAllocationInfo, &out->gpuBuffers[i].buffer, &out->gpuBuffers[i].allocation, nullptr)); + + /* copy staging buffer into both */ + copyBuffer(pimpl->device.device, pimpl->device.commandPools.transfer, pimpl->device.queues.transferQueues[0], out->stagingBuffer.buffer, out->gpuBuffers[i].buffer, out->stagingBuffer.size); + } + + return out; + + } + + void GFXDevice::destroyDescriptorBuffer(const gfx::DescriptorBuffer* descriptorBuffer) + { + for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) { + vmaDestroyBuffer(pimpl->allocator, descriptorBuffer->gpuBuffers[i].buffer, descriptorBuffer->gpuBuffers[i].allocation); + } + + vmaDestroyBuffer(pimpl->allocator, descriptorBuffer->stagingBuffer.buffer, descriptorBuffer->stagingBuffer.allocation); + + delete descriptorBuffer; + } + + void GFXDevice::writeDescriptorBuffer(gfx::DescriptorBuffer* buffer, uint64_t offset, uint64_t size, const void* data) + { + assert(offset + size <= buffer->stagingBuffer.size); + + /* first update the staging buffer */ + void* dataDest; + VKCHECK(vmaMapMemory(pimpl->allocator, buffer->stagingBuffer.allocation, &dataDest)); + memcpy(dataDest, (uint8_t*)data + offset, size); + vmaUnmapMemory(pimpl->allocator, buffer->stagingBuffer.allocation); + + /* queue the writes to each gpu buffer */ + // This is required as buffers cannot be updated if they are currently in use + for (uint32_t i = 0; i < FRAMES_IN_FLIGHT; i++) { + pimpl->descriptorBufferWriteQueues[i].insert(buffer); } -#endif } @@ -1178,8 +1273,8 @@ namespace engine { auto out = new gfx::Buffer{}; out->size = size; - out->type = type; + out->hostVisible = false; VkBuffer stagingBuffer; VmaAllocation stagingAllocation; @@ -1457,6 +1552,11 @@ namespace engine { #endif } + uint64_t GFXDevice::getFrameCount() + { + return pimpl->FRAMECOUNT; + } + void GFXDevice::waitIdle() { vkDeviceWaitIdle(pimpl->device.device); diff --git a/src/resources/shader.cpp b/src/resources/shader.cpp index 52b61e7..b488a24 100644 --- a/src/resources/shader.cpp +++ b/src/resources/shader.cpp @@ -1,5 +1,6 @@ #include "resources/shader.hpp" +#include "application.hpp" #include "gfx_device.hpp" #include "log.hpp" @@ -8,12 +9,10 @@ namespace engine::resources { - Shader::Shader(GFXDevice* gfx, const char* vertPath, const char* fragPath, const VertexParams& vertexParams, bool alphaBlending, bool cullBackFace) - : m_gfx(gfx) + Shader::Shader(RenderData* renderData, const char* vertPath, const char* fragPath, const VertexParams& vertexParams, bool alphaBlending, bool cullBackFace) + : m_gfx(renderData->gfxdev.get()) { - m_setZeroLayout = m_gfx->createDescriptorSetLayout(); - uint32_t index = 0; uint32_t stride = 0; gfx::VertexFormat vertFormat{}; @@ -45,26 +44,17 @@ namespace engine::resources { info.vertexFormat = vertFormat; info.alphaBlending = alphaBlending; info.backfaceCulling = cullBackFace; - info.descriptorSetLayouts.push_back(m_setZeroLayout); + info.descriptorSetLayouts.push_back(renderData->setZeroLayout); m_pipeline = m_gfx->createPipeline(info); LOG_INFO("Loaded shader: {}, vertex attribs: {}", vertPath, vertFormat.attributeDescriptions.size()); - /* allocate uniform descriptor set */ - m_setZero = m_gfx->allocateDescriptorSet(m_setZeroLayout); - /* fill with data */ - glm::vec4 myValue = { 0.5f, 0.5f, 0.5f, 1.0f }; - m_setZeroBuffer = m_gfx->createBuffer(gfx::BufferType::UNIFORM, sizeof(glm::vec4), &myValue); - m_gfx->updateDescriptor(m_setZero, 0, m_setZeroBuffer); - } Shader::~Shader() { - m_gfx->destroyBuffer(m_setZeroBuffer); m_gfx->destroyPipeline(m_pipeline); - m_gfx->destroyDescriptorSetLayout(m_setZeroLayout); } const gfx::Pipeline* Shader::getPipeline() diff --git a/src/systems/render.cpp b/src/systems/render.cpp index 9a6ed82..d2ea093 100644 --- a/src/systems/render.cpp +++ b/src/systems/render.cpp @@ -15,20 +15,22 @@ namespace engine { RenderSystem::RenderSystem(Scene* scene) - : System(scene, { typeid(TransformComponent).hash_code(), typeid(RenderableComponent).hash_code() }) + : System(scene, { typeid(TransformComponent).hash_code(), typeid(RenderableComponent).hash_code() }), + m_gfx(m_scene->app()->gfx()) { + } + RenderSystem::~RenderSystem() + { } void RenderSystem::onUpdate(float ts) { (void)ts; - - GFXDevice* const gfx = m_scene->app()->gfx(); - gfx::DrawBuffer* drawBuffer = m_scene->app()->getDrawBuffer(); + RenderData& renderData = m_scene->app()->renderData; + /* camera stuff */ - const auto cameraTransform = m_scene->getComponent(m_camera.camEntity); // do not render if camera is not set @@ -38,14 +40,20 @@ namespace engine { if (m_scene->app()->window()->getWindowResized()) { uint32_t w, h; - gfx->getViewportSize(&w, &h); + m_gfx->getViewportSize(&w, &h); m_viewportAspectRatio = (float)w / (float)h; } const float verticalFovRadians = glm::radians(m_camera.verticalFovDegrees); -// const float horizontalFovRadians = glm::radians(m_camera.horizontalFovDegrees); -// const float verticalFov = glm::atan( glm::tan(horizontalFovRadians / 2.0f) / m_viewportAspectRatio ) * 2.0f; const glm::mat4 projMatrix = glm::perspectiveZO(verticalFovRadians, m_viewportAspectRatio, m_camera.clipNear, m_camera.clipFar); + /* update SET 0 */ + RenderData::SetZeroBuffer uniform{}; + uniform.proj = projMatrix; + uniform.myValue.x = 1.0f; + m_value = glm::mod(m_value + ts, 1.0f); + uniform.myValue.y = m_value; + m_gfx->writeDescriptorBuffer(renderData.setZeroBuffer, 0, sizeof(RenderData::SetZeroBuffer), &uniform); + /* render all renderable entities */ for (uint32_t entity : m_entities) { @@ -59,16 +67,6 @@ namespace engine { assert(r->mesh != nullptr); //assert(r->material->m_texture != nullptr); - struct { - glm::mat4 proj; - glm::mat4 view; - } uniform{}; - - uniform.proj = projMatrix; - uniform.view = viewMatrix; - - gfx->updateUniformBuffer(r->material->getShader()->getPipeline(), &uniform, sizeof(glm::mat4) * 2, 0); - struct { glm::mat4 model; glm::mat4 view; @@ -77,21 +75,12 @@ namespace engine { pushConsts.model = t->worldMatrix; pushConsts.view = viewMatrix; - gfx->cmdBindPipeline(drawBuffer, r->material->getShader()->getPipeline()); - gfx->cmdBindDescriptorSet(drawBuffer, r->material->getShader()->getPipeline(), r->material->getShader()->getSetZero(), 0); - gfx->cmdBindVertexBuffer(drawBuffer, 0, r->mesh->getVB()); - gfx->cmdBindIndexBuffer(drawBuffer, r->mesh->getIB()); - gfx->cmdDrawIndexed(drawBuffer, r->mesh->getCount(), 1, 0, 0, 0); - - /* - gfx->draw( - r->material->getShader()->getPipeline(), - r->mesh->getVB(), - r->mesh->getIB(), - r->mesh->getCount(), - &pushConsts, - sizeof(pushConsts) - );*/ + m_gfx->cmdBindPipeline(renderData.drawBuffer, r->material->getShader()->getPipeline()); + m_gfx->cmdBindDescriptorSet(renderData.drawBuffer, r->material->getShader()->getPipeline(), renderData.setZero, 0); + m_gfx->cmdPushConstants(renderData.drawBuffer, r->material->getShader()->getPipeline(), 0, sizeof(pushConsts), &pushConsts); + m_gfx->cmdBindVertexBuffer(renderData.drawBuffer, 0, r->mesh->getVB()); + m_gfx->cmdBindIndexBuffer(renderData.drawBuffer, r->mesh->getIB()); + m_gfx->cmdDrawIndexed(renderData.drawBuffer, r->mesh->getCount(), 1, 0, 0, 0); } diff --git a/src/vulkan/swapchain.cpp b/src/vulkan/swapchain.cpp index 20f91b7..2c527dc 100644 --- a/src/vulkan/swapchain.cpp +++ b/src/vulkan/swapchain.cpp @@ -5,6 +5,8 @@ #include +#include "log.hpp" + #include "swapchain.h" namespace engine { @@ -13,7 +15,7 @@ namespace engine { { sc->device = info.device; - printf("Recreating swapchain!\n"); + LOG_INFO("Recreating swapchain!\n"); // get surface caps and features VkResult res; diff --git a/test/src/game.cpp b/test/src/game.cpp index 612805c..5e16f48 100644 --- a/test/src/game.cpp +++ b/test/src/game.cpp @@ -57,32 +57,6 @@ void playGame(bool enableFrameLimiter) auto myScene = app.sceneManager()->createEmptyScene(); - const std::string vertPath = app.getResourcePath("engine/shaders/test.vert"); - const std::string fragPath = app.getResourcePath("engine/shaders/test.frag"); - engine::resources::Shader::VertexParams vertexParams{}; - vertexParams.hasColor = false; - vertexParams.hasNormal = true; - vertexParams.hasTangent = false; - vertexParams.hasUV0 = true; - bool alphaBlending = false; - bool cullBackFace = true; - auto testShader = std::make_shared(app.gfx(), vertPath.c_str(), fragPath.c_str(), vertexParams, alphaBlending, cullBackFace); - - auto camera = myScene->createEntity("camera"); - - auto renderSystem = myScene->getSystem(); - renderSystem->setCameraEntity(camera); - myScene->getComponent(camera)->position = { 0.0f, 10.0f, 0.0f }; - - /* cube */ - uint32_t cube = myScene->createEntity("cube"); - myScene->getComponent(cube)->position = glm::vec3{ -0.5f, -0.5f, -0.5f }; - auto cubeRenderable = myScene->addComponent(cube); - cubeRenderable->material = std::make_shared(testShader); - cubeRenderable->mesh = genCuboidMesh(app.gfx(), 1, 1.0f, 1, 1); - -#if 0 - /* create camera */ { myScene->registerComponent(); @@ -130,6 +104,15 @@ void playGame(bool enableFrameLimiter) myScene->getComponent(skybox)->position = { -1.0f, -1.0f, -1.0f }; } + /* cube */ + { + uint32_t cube = myScene->createEntity("cube"); + myScene->getComponent(cube)->position = glm::vec3{ -0.5f, -0.5f, -0.5f }; + auto cubeRenderable = myScene->addComponent(cube); + cubeRenderable->material = std::make_shared(app.getResource("builtin.standard")); + cubeRenderable->mesh = genCuboidMesh(app.gfx(), 1.0f, 1.0f, 1.0f, 1); + } + /* floor */ { uint32_t floor = myScene->createEntity("floor"); @@ -138,13 +121,12 @@ void playGame(bool enableFrameLimiter) floorRenderable->material = std::make_shared(app.getResource("builtin.standard")); floorRenderable->material->m_texture = grassTexture; floorRenderable->mesh = genCuboidMesh(app.gfx(), 10000.0f, 1.0f, 10000.0f, 5000.0f); + floorRenderable->shown = true; auto floorCollider = myScene->addComponent(floor); floorCollider->isStatic = true; floorCollider->aabb = { { 0.0f, 0.0f, 0.0f }, { 10000.0f, 1.0f, 10000.0f } }; } -#endif - app.gameLoop(); }