Make uniform buffers updateable.

This commit is contained in:
bailwillharr 2023-03-13 17:10:46 +00:00
parent bbf582d85f
commit fa34a8c583
19 changed files with 303 additions and 227 deletions

View File

@ -4,6 +4,8 @@
#include "gfx.hpp"
#include <glm/mat4x4.hpp>
#include <memory>
#include <string>
#include <filesystem>
@ -22,6 +24,19 @@ namespace engine {
class Texture;
}
struct RenderData {
std::unique_ptr<GFXDevice> 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<Window> m_window;
std::unique_ptr<GFXDevice> m_gfx;
std::unique_ptr<InputManager> m_inputManager;
std::unique_ptr<SceneManager> m_sceneManager;
@ -79,8 +93,6 @@ namespace engine {
bool m_enableFrameLimiter = true;
gfx::DrawBuffer *m_drawBuffer;
/* resource stuff */
std::unordered_map<size_t, std::unique_ptr<IResourceManager>> m_resourceManagers{};

View File

@ -9,6 +9,7 @@ namespace engine::gfx {
// handles (incomplete types)
struct Pipeline;
struct DescriptorBuffer;
struct Buffer;
struct Texture;
struct DrawBuffer;

View File

@ -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();

View File

@ -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;
};

View File

@ -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;
};
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}

View File

@ -14,6 +14,8 @@
#include "resources/shader.hpp"
#include "resources/texture.hpp"
#include <glm/gtc/matrix_transform.hpp>
// To allow the FPS-limiter to put the thread to sleep
#include <thread>
@ -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<Window>(appName, true, false);
m_gfx = std::make_unique<GFXDevice>(appName, appVersion, m_window->getHandle(), graphicsSettings);
m_inputManager = std::make_unique<InputManager>(window());
m_sceneManager = std::make_unique<SceneManager>(this);
@ -72,14 +73,24 @@ namespace engine {
registerResourceManager<resources::Material>();
registerResourceManager<resources::Mesh>();
// initialise the render data
renderData.gfxdev = std::make_unique<GFXDevice>(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<resources::Shader>(
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<resources::Shader>(
gfx(),
&renderData,
getResourcePath("engine/shaders/skybox.vert").c_str(),
getResourcePath("engine/shaders/skybox.frag").c_str(),
vertParams,
@ -102,7 +113,6 @@ namespace engine {
);
getResourceManager<resources::Shader>()->addPersistent("builtin.skybox", std::move(texturedShader));
}
#endif
{
auto whiteTexture = std::make_unique<resources::Texture>(
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();
}
}

View File

@ -6,7 +6,7 @@
#include <fstream>
#include <filesystem>
#include <optional>
#include <queue>
#include <deque>
#include <map>
#include <iostream>
@ -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<VkDescriptorSet, FRAMES_IN_FLIGHT> sets; // frames in flight cannot use the same descriptor set in case the buffer needs updating
};
struct gfx::DescriptorBuffer {
gfx::Buffer stagingBuffer{};
std::array<gfx::Buffer, FRAMES_IN_FLIGHT> gpuBuffers;
};
// enum converters
@ -552,6 +559,7 @@ namespace engine {
Swapchain swapchain{};
VkDescriptorPool descriptorPool;
std::array<std::unordered_set<gfx::DescriptorBuffer*>, 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);

View File

@ -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()

View File

@ -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<TransformComponent>(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);
}

View File

@ -5,6 +5,8 @@
#include <SDL2/SDL_vulkan.h>
#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;

View File

@ -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<engine::resources::Shader>(app.gfx(), vertPath.c_str(), fragPath.c_str(), vertexParams, alphaBlending, cullBackFace);
auto camera = myScene->createEntity("camera");
auto renderSystem = myScene->getSystem<engine::RenderSystem>();
renderSystem->setCameraEntity(camera);
myScene->getComponent<engine::TransformComponent>(camera)->position = { 0.0f, 10.0f, 0.0f };
/* cube */
uint32_t cube = myScene->createEntity("cube");
myScene->getComponent<engine::TransformComponent>(cube)->position = glm::vec3{ -0.5f, -0.5f, -0.5f };
auto cubeRenderable = myScene->addComponent<engine::RenderableComponent>(cube);
cubeRenderable->material = std::make_shared<engine::resources::Material>(testShader);
cubeRenderable->mesh = genCuboidMesh(app.gfx(), 1, 1.0f, 1, 1);
#if 0
/* create camera */
{
myScene->registerComponent<CameraControllerComponent>();
@ -130,6 +104,15 @@ void playGame(bool enableFrameLimiter)
myScene->getComponent<engine::TransformComponent>(skybox)->position = { -1.0f, -1.0f, -1.0f };
}
/* cube */
{
uint32_t cube = myScene->createEntity("cube");
myScene->getComponent<engine::TransformComponent>(cube)->position = glm::vec3{ -0.5f, -0.5f, -0.5f };
auto cubeRenderable = myScene->addComponent<engine::RenderableComponent>(cube);
cubeRenderable->material = std::make_shared<engine::resources::Material>(app.getResource<engine::resources::Shader>("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<engine::resources::Material>(app.getResource<engine::resources::Shader>("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<engine::ColliderComponent>(floor);
floorCollider->isStatic = true;
floorCollider->aabb = { { 0.0f, 0.0f, 0.0f }, { 10000.0f, 1.0f, 10000.0f } };
}
#endif
app.gameLoop();
}