diff --git a/aster/buffer.cpp b/aster/buffer.cpp index 4564cca..c390304 100644 --- a/aster/buffer.cpp +++ b/aster/buffer.cpp @@ -91,12 +91,20 @@ UniformBuffer::Init(const Device *device, const usize size, const cstr name) } void -UniformStorageBuffer::Init(const Device *device, usize size, cstr name) +StorageBuffer::Init(const Device *device, usize size, bool hostVisible, cstr name) { - Allocate(device, size, vk::BufferUsageFlagBits::eStorageBuffer, - VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | - VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT, - VMA_MEMORY_USAGE_AUTO, name); + if (hostVisible) + { + Allocate(device, size, vk::BufferUsageFlagBits::eStorageBuffer, + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT, + VMA_MEMORY_USAGE_AUTO, name); + } + else + { + Allocate(device, size, vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferDst, + VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name); + } } void @@ -106,6 +114,13 @@ VertexBuffer::Init(const Device *device, usize size, cstr name) VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name); } +void +IndexBuffer::Init(const Device *device, usize size, cstr name) +{ + Allocate(device, size, vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst, + VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name); +} + void StagingBuffer::Init(const Device *device, usize size, cstr name) { diff --git a/aster/buffer.h b/aster/buffer.h index 6ef3030..57b3076 100644 --- a/aster/buffer.h +++ b/aster/buffer.h @@ -41,9 +41,9 @@ struct UniformBuffer : Buffer void Init(const Device *device, usize size, cstr name = nullptr); }; -struct UniformStorageBuffer : Buffer +struct StorageBuffer : Buffer { - void Init(const Device *device, usize size, cstr name = nullptr); + void Init(const Device *device, usize size, bool hostVisible, cstr name = nullptr); }; struct VertexBuffer : Buffer @@ -52,6 +52,12 @@ struct VertexBuffer : Buffer void Write(const Device *device, void *data, usize size, usize offset) const = delete; }; +struct IndexBuffer : Buffer +{ + void Init(const Device *device, usize size, cstr name = nullptr); + void Write(const Device *device, void *data, usize size, usize offset) const = delete; +}; + struct StagingBuffer : Buffer { void Init(const Device *device, usize size, cstr name = nullptr); diff --git a/aster/global.h b/aster/global.h index 18394fd..b6cf119 100644 --- a/aster/global.h +++ b/aster/global.h @@ -40,6 +40,8 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3; #define Take(ELEMENT) eastl::exchange(ELEMENT, {}) +#define TODO(MSG) assert(false && ("Unimplemented: " MSG)) + [[nodiscard]] inline bool Failed(const vk::Result result) { diff --git a/aster/image.cpp b/aster/image.cpp index 5a86ec1..b625e96 100644 --- a/aster/image.cpp +++ b/aster/image.cpp @@ -68,6 +68,7 @@ Texture::Init(const Device *device, const vk::Extent2D extent, vk::Format imageF m_View = view; m_Allocation = allocation; m_Extent = {extent.width, extent.height, 1}; + m_MipLevels = mipLevels; device->SetName(m_Image, name); } diff --git a/aster/image.h b/aster/image.h index 6a3208e..c2c1d3d 100644 --- a/aster/image.h +++ b/aster/image.h @@ -15,6 +15,7 @@ struct Image vk::ImageView m_View = nullptr; VmaAllocation m_Allocation = nullptr; vk::Extent3D m_Extent; + u8 m_MipLevels = 1; [[nodiscard]] bool IsValid() const; diff --git a/samples/03_model_render/CMakeLists.txt b/samples/03_model_render/CMakeLists.txt index 37f497a..d8b64f2 100644 --- a/samples/03_model_render/CMakeLists.txt +++ b/samples/03_model_render/CMakeLists.txt @@ -11,7 +11,8 @@ add_executable(model_render model_render.cpp render_resource_manager.cpp render_resource_manager.h model_loader.cpp - model_loader.h) + model_loader.h +) add_shader(model_render shader/model.vert.glsl) add_shader(model_render shader/model.frag.glsl) diff --git a/samples/03_model_render/model_loader.cpp b/samples/03_model_render/model_loader.cpp index 1f2f1ee..c020d72 100644 --- a/samples/03_model_render/model_loader.cpp +++ b/samples/03_model_render/model_loader.cpp @@ -12,16 +12,183 @@ #include "model_loader.h" +#include "buffer.h" +#include "device.h" +#include "helpers.h" +#include "image.h" +#include "render_resource_manager.h" + +#include +#include +#include + +vec4 +VectorToVec4(const std::vector &vec) +{ + if (vec.empty()) + { + return vec4{0.0f}; + } + + return {vec[0], vec[1], vec[2], vec[3]}; +} + +TextureHandle +ModelLoader::LoadImage(vk::CommandBuffer commandBuffer, Texture *texture, StagingBuffer *stagingBuffer, + tinygltf::Image *image) +{ + assert(image->component == 4); + + texture->Init(m_ResourceManager->m_Device, {.width = Cast(image->width), .height = Cast(image->height)}, + vk::Format::eR8G8B8A8Srgb, true, image->name.data()); + + // .srcAccessMask = , + // .dstAccessMask = , + // .oldLayout = , + // .newLayout = , + // .srcQueueFamilyIndex = , + // .dstQueueFamilyIndex = , + // .image = , + // .subresourceRange = + vk::ImageMemoryBarrier imageStartBarrier = { + .srcAccessMask = vk::AccessFlagBits::eNone, + .dstAccessMask = vk::AccessFlagBits::eTransferWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eTransferDstOptimal, + .srcQueueFamilyIndex = vk::QueueFamilyIgnored, + .dstQueueFamilyIndex = vk::QueueFamilyIgnored, + .image = texture->m_Image, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = texture->m_MipLevels, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + + vk::ImageMemoryBarrier nextMipBarrier = { + .srcAccessMask = vk::AccessFlagBits::eTransferWrite, + .dstAccessMask = vk::AccessFlagBits::eTransferRead, + .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::eTransferSrcOptimal, + .srcQueueFamilyIndex = vk::QueueFamilyIgnored, + .dstQueueFamilyIndex = vk::QueueFamilyIgnored, + .image = texture->m_Image, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + + vk::ImageMemoryBarrier imageReadyBarrier = { + .srcAccessMask = vk::AccessFlagBits::eTransferRead, + .dstAccessMask = vk::AccessFlagBits::eShaderRead, + .oldLayout = vk::ImageLayout::eTransferSrcOptimal, + .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + .srcQueueFamilyIndex = m_TransferQueueIndex, + .dstQueueFamilyIndex = m_GraphicsQueueIndex, + .image = texture->m_Image, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = texture->m_MipLevels, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + + vk::BufferImageCopy imageCopy = { + .bufferOffset = 0, + .bufferRowLength = Cast(image->width), + .bufferImageHeight = Cast(image->height), + .imageSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .imageOffset = {}, + .imageExtent = texture->m_Extent, + }; + + commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, {}, 0, + nullptr, 0, nullptr, 1, &imageStartBarrier); + commandBuffer.copyBufferToImage(stagingBuffer->m_Buffer, texture->m_Image, vk::ImageLayout::eTransferDstOptimal, 1, + &imageCopy); + commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer, {}, 0, + nullptr, 0, nullptr, 1, &nextMipBarrier); + + auto calcNextMip = [](i32 prev) { return eastl::max(prev / 2, 1); }; + + i32 prevMipWidth = Cast(texture->m_Extent.width); + i32 prevMipHeight = Cast(texture->m_Extent.height); + + for (u32 prevMipLevel = 1; prevMipLevel < texture->m_MipLevels; ++prevMipLevel) + { + i32 currentMipWidth = calcNextMip(prevMipWidth); + i32 currentMipHeight = calcNextMip(prevMipHeight); + u32 currentMipLevel = prevMipLevel + 1; + + vk::ImageBlit blitRegion = { + .srcSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = prevMipLevel, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .srcOffsets = + std::array{ + vk::Offset3D{0, 0, 0}, + vk::Offset3D{prevMipWidth, prevMipHeight, 1}, + }, + .dstSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = currentMipLevel, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .dstOffsets = + std::array{ + vk::Offset3D{0, 0, 0}, + vk::Offset3D{currentMipWidth, currentMipHeight, 1}, + }, + }; + + nextMipBarrier.subresourceRange.baseMipLevel = prevMipLevel; + commandBuffer.blitImage(texture->m_Image, vk::ImageLayout::eTransferSrcOptimal, texture->m_Image, + vk::ImageLayout::eTransferDstOptimal, 1, &blitRegion, vk::Filter::eLinear); + commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer, {}, 0, + nullptr, 0, nullptr, 1, &nextMipBarrier); + } + + commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTopOfPipe, {}, 0, + nullptr, 0, nullptr, 1, &imageReadyBarrier); + + return m_ResourceManager->Commit(texture); +} + Model -LoadModel(RenderResourceManager *resourceManager, cstr path) +ModelLoader::LoadModel(cstr path, cstr name) { namespace fs = std::filesystem; tinygltf::Model model; tinygltf::TinyGLTF loader; + const Device *pDevice = m_ResourceManager->m_Device; + const auto fsPath = fs::absolute(path); const auto ext = fsPath.extension(); - if (ext.c_str() == GLTF_ASCII_FILE_EXTENSION) + if (ext == GLTF_ASCII_FILE_EXTENSION) { std::string err; std::string warn; @@ -31,7 +198,7 @@ LoadModel(RenderResourceManager *resourceManager, cstr path) ELSE_IF_WARN(!warn.empty(), "{}", warn); } } - if (ext.c_str() == GLTF_BINARY_FILE_EXTENSION) + if (ext == GLTF_BINARY_FILE_EXTENSION) { std::string err; std::string warn; @@ -41,4 +208,309 @@ LoadModel(RenderResourceManager *resourceManager, cstr path) ELSE_IF_WARN(!warn.empty(), "{}", warn); } } + + vk::CommandBuffer commandBuffer; + { + vk::CommandBufferAllocateInfo commandBufferAllocateInfo = { + .commandPool = m_CommandPool, + .level = vk::CommandBufferLevel::ePrimary, + .commandBufferCount = 1, + }; + AbortIfFailed(pDevice->m_Device.allocateCommandBuffers(&commandBufferAllocateInfo, &commandBuffer)); + + vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; + AbortIfFailed(commandBuffer.begin(&beginInfo)); + } + + eastl::vector stagingBuffers; + eastl::vector textures; + eastl::vector textureHandles; + + if (!model.images.empty()) + { + u32 numImages = model.images.size(); + + stagingBuffers.resize(numImages); + textures.resize(numImages); + textureHandles.resize(numImages); + + auto stagingPtr = stagingBuffers.data(); + auto texturePtr = textures.data(); + auto imagePtr = model.images.data(); + for (TextureHandle &handle : textureHandles) + { + handle = LoadImage(commandBuffer, texturePtr++, stagingPtr++, imagePtr++); + } + + AbortIfFailed(commandBuffer.end()); + } + + eastl::vector materials; + StorageBuffer materialsBuffer; + BufferHandle materialsHandle; + + if (!model.materials.empty()) + { + auto getTextureHandle = [&textureHandles](i32 index) -> TextureHandle { + if (index >= 0) + { + return textureHandles[index]; + } + return {}; + }; + + materials.reserve(model.materials.size()); + for (auto &material : model.materials) + { + materials.push_back({ + .m_AlbedoFactor = VectorToVec4(material.pbrMetallicRoughness.baseColorFactor), + .m_EmissionFactor = VectorToVec4(material.emissiveFactor), + .m_MetalFactor = Cast(material.pbrMetallicRoughness.metallicFactor), + .m_RoughFactor = Cast(material.pbrMetallicRoughness.roughnessFactor), + .m_AlbedoTex = getTextureHandle(material.pbrMetallicRoughness.baseColorTexture.index), + .m_NormalTex = getTextureHandle(material.normalTexture.index), + .m_MetalRoughTex = getTextureHandle(material.pbrMetallicRoughness.metallicRoughnessTexture.index), + .m_OcclusionTex = getTextureHandle(material.occlusionTexture.index), + .m_EmissionTex = getTextureHandle(material.emissiveTexture.index), + }); + } + + usize materialsByteSize = materials.size() * sizeof materials[0]; + materialsBuffer.Init(pDevice, materialsByteSize, false, name); + materialsHandle = m_ResourceManager->Commit(&materialsBuffer); + + StagingBuffer &materialStaging = stagingBuffers.push_back(); + materialStaging.Init(pDevice, materialsByteSize); + materialStaging.Write(pDevice, 0, materialsByteSize, materials.data()); + + vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = materialsByteSize}; + commandBuffer.copyBuffer(materialStaging.m_Buffer, materialsBuffer.m_Buffer, 1, &bufferCopy); + } + + // TODO: Mesh reordering based on nodes AND OR meshoptimizer + // TODO: Support scenes + + eastl::vector vertexPositions; + eastl::vector indices; + eastl::vector meshPrimitives; + meshPrimitives.reserve(model.meshes.size()); + + for (auto &mesh : model.meshes) + { + for (auto &prim : mesh.primitives) + { + u32 vertexOffset = vertexPositions.size(); + u32 vertexCount; + u32 indexOffset = indices.size(); + u32 indexCount; + + assert(prim.attributes.contains(APosition)); + assert(prim.mode == TINYGLTF_MODE_TRIANGLES); + { + tinygltf::Accessor *posAccessor = &model.accessors[prim.attributes[APosition]]; + + assert(posAccessor->count <= MaxValue); + + tinygltf::BufferView *posBufferView = &model.bufferViews[posAccessor->bufferView]; + tinygltf::Buffer *posBuffer = &model.buffers[posBufferView->buffer]; + usize byteOffset = (posAccessor->byteOffset + posBufferView->byteOffset); + + vertexCount = posAccessor->count; + vertexPositions.reserve(vertexOffset + vertexCount); + + if (posAccessor->type == TINYGLTF_TYPE_VEC4) + { + vec4 *data = Recast(posBuffer->data.data() + byteOffset); + vertexPositions.insert(vertexPositions.end(), data, data + vertexCount); + } + else if (posAccessor->type == TINYGLTF_TYPE_VEC3) + { + vec3 *data = Recast(posBuffer->data.data() + byteOffset); + for (u32 i = 0; i < vertexCount; ++i) + { + vertexPositions.push_back(vec4(data[i], 1.0f)); + } + } + else if (posAccessor->type == TINYGLTF_TYPE_VEC2) + { + vec2 *data = Recast(posBuffer->data.data() + byteOffset); + for (u32 i = 0; i < vertexCount; ++i) + { + vertexPositions.push_back(vec4(data[i], 0.0f, 1.0f)); + } + } + } + + if (prim.indices >= 0) + { + tinygltf::Accessor *indexAccessor = &model.accessors[prim.indices]; + + assert(indexAccessor->count <= MaxValue); + + tinygltf::BufferView *indexBufferView = &model.bufferViews[indexAccessor->bufferView]; + tinygltf::Buffer *indexBuffer = &model.buffers[indexBufferView->buffer]; + usize byteOffset = (indexAccessor->byteOffset + indexBufferView->byteOffset); + + indexCount = indexAccessor->count; + indices.reserve(indexOffset + indexCount); + + if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) + { + u32 *data = Recast(indexBuffer->data.data() + byteOffset); + indices.insert(indices.end(), data, data + indexCount); + } + else if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) + { + u16 *data = Recast(indexBuffer->data.data() + byteOffset); + indices.insert(indices.end(), data, data + indexCount); + } + else if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) + { + u8 *data = Recast(indexBuffer->data.data() + byteOffset); + indices.insert(indices.end(), data, data + indexCount); + } + } + else + { + indexCount = vertexCount; + indices.reserve(indexOffset + vertexCount); + for (u32 i = 0; i < indexCount; ++i) + { + indices.push_back(i); + } + } + + meshPrimitives.push_back({ + .m_VertexOffset = vertexOffset, + .m_FirstIndex = indexOffset, + .m_IndexCount = indexCount, + .m_MaterialIdx = prim.material, + }); + } + } + + StorageBuffer positionBuffer; + positionBuffer.Init(pDevice, vertexPositions.size() * sizeof vertexPositions[0], false); + BufferHandle positionBufferHandle = m_ResourceManager->Commit(&positionBuffer); + + IndexBuffer indexBuffer; + indexBuffer.Init(pDevice, indices.size() * sizeof indices[0]); + + { + vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0}; + + StagingBuffer &positionStaging = stagingBuffers.push_back(); + positionStaging.Init(pDevice, positionBuffer.GetSize()); + positionStaging.Write(pDevice, 0, positionBuffer.GetSize(), vertexPositions.data()); + bufferCopy.size = positionBuffer.GetSize(); + commandBuffer.copyBuffer(positionStaging.m_Buffer, positionBuffer.m_Buffer, 1, &bufferCopy); + + StagingBuffer &indexStaging = stagingBuffers.push_back(); + indexStaging.Init(pDevice, indexBuffer.GetSize()); + indexStaging.Write(pDevice, 0, indexBuffer.GetSize(), indices.data()); + bufferCopy.size = indexBuffer.GetSize(); + commandBuffer.copyBuffer(indexStaging.m_Buffer, indexBuffer.m_Buffer, 1, &bufferCopy); + } + + AbortIfFailed(commandBuffer.end()); + + vk::SubmitInfo submitInfo = { + .waitSemaphoreCount = 0, + .pWaitDstStageMask = 0, + .commandBufferCount = 1, + .pCommandBuffers = &commandBuffer, + }; + + vk::Fence fence; + vk::FenceCreateInfo fenceCreateInfo = {}; + AbortIfFailed(pDevice->m_Device.createFence(&fenceCreateInfo, nullptr, &fence)); + AbortIfFailed(m_TransferQueue.submit(1, &submitInfo, fence)); + AbortIfFailed(pDevice->m_Device.waitForFences(1, &fence, true, MaxValue)); + pDevice->m_Device.destroy(fence, nullptr); + + for (auto &buffer : stagingBuffers) + { + buffer.Destroy(pDevice); + } + + return Model{m_ResourceManager, std::move(textures), std::move(textureHandles), + materialsBuffer, materialsHandle, positionBuffer, + positionBufferHandle, indexBuffer, meshPrimitives}; +} + +Model::Model(RenderResourceManager *resourceManager, eastl::vector &&textures, + eastl::vector &&textureHandles, const StorageBuffer &materialsBuffer, + BufferHandle materialsHandle, const StorageBuffer &vertexPosBuffer, BufferHandle vertexPosHandle, + const IndexBuffer &indexBuffer, const eastl::vector &meshPrimitives) + : m_ResourceManager(resourceManager) + , m_Textures(textures) + , m_TextureHandles(textureHandles) + , m_MaterialsBuffer(materialsBuffer) + , m_MaterialsHandle(materialsHandle) + , m_VertexPositions(vertexPosBuffer) + , m_VertexPositionHandle(vertexPosHandle) + , m_IndexBuffer(indexBuffer) + , m_MeshPrimitives(meshPrimitives) +{ +} + +Model::Model(Model &&other) noexcept + : m_ResourceManager(Take(other.m_ResourceManager)) + , m_Textures(std::move(other.m_Textures)) + , m_TextureHandles(std::move(other.m_TextureHandles)) + , m_MaterialsBuffer(other.m_MaterialsBuffer) + , m_MaterialsHandle(other.m_MaterialsHandle) + , m_VertexPositions(other.m_VertexPositions) + , m_VertexPositionHandle(other.m_VertexPositionHandle) + , m_IndexBuffer(other.m_IndexBuffer) + , m_MeshPrimitives(std::move(other.m_MeshPrimitives)) +{ +} + +Model & +Model::operator=(Model &&other) noexcept +{ + if (this == &other) + return *this; + m_ResourceManager = Take(other.m_ResourceManager); + m_Textures = std::move(other.m_Textures); + m_TextureHandles = std::move(other.m_TextureHandles); + m_MaterialsBuffer = other.m_MaterialsBuffer; + m_MaterialsHandle = other.m_MaterialsHandle; + m_VertexPositions = other.m_VertexPositions; + m_VertexPositionHandle = other.m_VertexPositionHandle; + m_IndexBuffer = other.m_IndexBuffer; + m_MeshPrimitives = std::move(other.m_MeshPrimitives); + return *this; +} + +Model::~Model() +{ + if (!m_ResourceManager) + return; + + m_VertexPositions.Destroy(m_ResourceManager->m_Device); + m_IndexBuffer.Destroy(m_ResourceManager->m_Device); + + m_ResourceManager->Release(m_VertexPositionHandle); + for (const TextureHandle &handle : m_TextureHandles) + { + m_ResourceManager->Release(handle); + } + m_ResourceManager->Release(m_MaterialsHandle); + for (Texture &texture : m_Textures) + { + texture.Destroy(m_ResourceManager->m_Device); + } + m_MaterialsBuffer.Destroy(m_ResourceManager->m_Device); +} + +ModelLoader::ModelLoader(RenderResourceManager *resourceManager, vk::CommandPool commandPool, vk::Queue transferQueue, + u32 transferQueueIndex, u32 graphicsQueueIndex) + : m_ResourceManager(resourceManager) + , m_CommandPool(commandPool) + , m_TransferQueue(transferQueue) + , m_TransferQueueIndex(transferQueueIndex) + , m_GraphicsQueueIndex(graphicsQueueIndex) +{ } \ No newline at end of file diff --git a/samples/03_model_render/model_loader.h b/samples/03_model_render/model_loader.h index 87b103b..ac2ea7e 100644 --- a/samples/03_model_render/model_loader.h +++ b/samples/03_model_render/model_loader.h @@ -5,26 +5,86 @@ #pragma once -#include "constants.h" +#include "buffer.h" +#include "global.h" +#include "image.h" + +#include "render_resource_manager.h" -#include #include -struct RenderResourceManager; +struct TextureHandle; struct Texture; + constexpr auto GLTF_ASCII_FILE_EXTENSION = ".gltf"; constexpr auto GLTF_BINARY_FILE_EXTENSION = ".glb"; -constexpr static auto NORMAL = "NORMAL"; -constexpr static auto POSITION = "POSITION"; -constexpr static auto TANGENT = "TANGENT"; -constexpr static auto TEXCOORD_0 = "TEXCOORD_0"; -constexpr static auto TEXCOORD_1 = "TEXCOORD_1"; -constexpr static auto JOINTS_0 = "JOINTS_0"; -constexpr static auto WEIGHTS_0 = "WEIGHTS_0"; +struct MeshPrimitive +{ + u32 m_VertexOffset; + u32 m_FirstIndex; + u32 m_IndexCount; + i32 m_MaterialIdx; // -1 for invalid +}; + +struct Material +{ + vec4 m_AlbedoFactor; + vec4 m_EmissionFactor; + f32 m_MetalFactor; + f32 m_RoughFactor; + TextureHandle m_AlbedoTex; + TextureHandle m_NormalTex; + TextureHandle m_MetalRoughTex; + TextureHandle m_OcclusionTex; + TextureHandle m_EmissionTex; +}; struct Model { + RenderResourceManager *m_ResourceManager; + eastl::vector m_Textures; + eastl::vector m_TextureHandles; + StorageBuffer m_MaterialsBuffer; + BufferHandle m_MaterialsHandle; + StorageBuffer m_VertexPositions; + BufferHandle m_VertexPositionHandle; + IndexBuffer m_IndexBuffer; + eastl::vector m_MeshPrimitives; + + Model(RenderResourceManager *resourceManager, eastl::vector &&textures, + eastl::vector &&textureHandles, const StorageBuffer &materialsBuffer, + BufferHandle materialsHandle, const StorageBuffer &vertexPosBuffer, BufferHandle vertexPosHandle, + const IndexBuffer &indexBuffer, const eastl::vector &meshPrimitives); + + Model(Model &&other) noexcept; + Model &operator=(Model &&other) noexcept; + + ~Model(); + + DISALLOW_COPY_AND_ASSIGN(Model); }; -Model LoadModel(RenderResourceManager *resourceManager, cstr path); \ No newline at end of file +struct ModelLoader +{ + RenderResourceManager *const m_ResourceManager; + vk::CommandPool m_CommandPool; + vk::Queue m_TransferQueue; + u32 m_TransferQueueIndex; + u32 m_GraphicsQueueIndex; + + ModelLoader(RenderResourceManager *resourceManager, vk::CommandPool commandPool, vk::Queue transferQueue, + u32 transferQueueIndex, u32 graphicsQueueIndex); + + TextureHandle + LoadImage(vk::CommandBuffer commandBuffer, Texture *texture, StagingBuffer *stagingBuffer, tinygltf::Image *image); + Model LoadModel(cstr path, cstr name = nullptr); + + constexpr static auto ANormal = "NORMAL"; + constexpr static auto APosition = "POSITION"; + constexpr static auto ATangent = "TANGENT"; + constexpr static auto ATexCoord0 = "TEXCOORD_0"; + constexpr static auto ATexCoord1 = "TEXCOORD_1"; + constexpr static auto AJoints0 = "JOINTS_0"; + constexpr static auto AWeights0 = "WEIGHTS_0"; +}; \ No newline at end of file diff --git a/samples/03_model_render/model_render.cpp b/samples/03_model_render/model_render.cpp index 79e789b..6d48374 100644 --- a/samples/03_model_render/model_render.cpp +++ b/samples/03_model_render/model_render.cpp @@ -31,7 +31,7 @@ constexpr auto MODEL_FILE = "model/Box.glb"; struct ImageFile { - void *m_Data = nullptr; + u8 *m_Data = nullptr; u32 m_Width = 0; u32 m_Height = 0; u32 m_NumChannels = 0; @@ -85,7 +85,31 @@ main(int, char **) Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"}; vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0); Swapchain swapchain = {&window, &device, "Primary Chain"}; - RenderResourceManager resourceManager(&device, 10); + RenderResourceManager resourceManager = {&device, 1000}; + + vk::CommandPool copyPool; + vk::CommandBuffer copyBuffer; + + { + vk::CommandPoolCreateInfo poolCreateInfo = { + .flags = vk::CommandPoolCreateFlagBits::eTransient, + .queueFamilyIndex = queueAllocation.m_Family, + }; + AbortIfFailedM(device.m_Device.createCommandPool(&poolCreateInfo, nullptr, ©Pool), + "Copy command pool creation failed."); + vk::CommandBufferAllocateInfo bufferAllocateInfo = { + .commandPool = copyPool, + .level = vk::CommandBufferLevel::ePrimary, + .commandBufferCount = 1, + }; + AbortIfFailedM(device.m_Device.allocateCommandBuffers(&bufferAllocateInfo, ©Buffer), + "Copy command buffer allocation failed."); + } + + ModelLoader modelLoader = {&resourceManager, copyPool, commandQueue, queueAllocation.m_Family, + queueAllocation.m_Family}; + + auto model = modelLoader.LoadModel(MODEL_FILE); Pipeline pipeline = CreatePipeline(&device, &swapchain, &resourceManager); @@ -123,25 +147,6 @@ main(int, char **) AbortIfFailed(device.m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &descriptorSet)); } - vk::CommandPool copyPool; - vk::CommandBuffer copyBuffer; - - { - vk::CommandPoolCreateInfo poolCreateInfo = { - .flags = vk::CommandPoolCreateFlagBits::eTransient, - .queueFamilyIndex = queueAllocation.m_Family, - }; - AbortIfFailedM(device.m_Device.createCommandPool(&poolCreateInfo, nullptr, ©Pool), - "Copy command pool creation failed."); - vk::CommandBufferAllocateInfo bufferAllocateInfo = { - .commandPool = copyPool, - .level = vk::CommandBufferLevel::ePrimary, - .commandBufferCount = 1, - }; - AbortIfFailedM(device.m_Device.allocateCommandBuffers(&bufferAllocateInfo, ©Buffer), - "Copy command buffer allocation failed."); - } - eastl::array vertices = { Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)}, @@ -192,14 +197,14 @@ main(int, char **) INFO("Image {}x{} : {} channels", crateImageFile.m_Width, crateImageFile.m_Height, crateImageFile.m_NumChannels); assert(plainImageFile.Load({0.7f, 0.4f, 0.1f, 1.0f})); - UniformStorageBuffer pvbo; + StorageBuffer pvbo; Texture crateTexture; Texture plainTexture; crateTexture.Init(&device, {crateImageFile.m_Width, crateImageFile.m_Height}, vk::Format::eR8G8B8A8Srgb, false, "Crate Texture"); plainTexture.Init(&device, {crateImageFile.m_Width, crateImageFile.m_Height}, vk::Format::eR8G8B8A8Srgb, false, "Plain Texture"); - pvbo.Init(&device, vertices.size() * sizeof vertices[0], "Pull VBO"); + pvbo.Init(&device, vertices.size() * sizeof vertices[0], true, "Pull VBO"); pvbo.Write(&device, 0, pvbo.GetSize(), vertices.data()); auto crateTextureId = resourceManager.Commit(&crateTexture); @@ -308,7 +313,6 @@ main(int, char **) 1, &imageCopy); copyBuffer.copyBufferToImage(imageStaging2.m_Buffer, plainTexture.m_Image, vk::ImageLayout::eTransferDstOptimal, 1, &imageCopy); - copyBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {}, 0, nullptr, 0, nullptr, Cast(imageReadyToRead.size()), imageReadyToRead.data()); @@ -423,8 +427,8 @@ main(int, char **) Time::Init(); - Handle *pushData = &crateTextureId; - Handle *otherPushData = &plainTextureId; + GpuResourceHandle *pushData = &crateTextureId; + GpuResourceHandle *otherPushData = &plainTextureId; bool prevPressed = false; auto isSpaceJustPressed = [&prevPressed, &window] { @@ -434,6 +438,17 @@ main(int, char **) return justPressed; }; + struct ModelData + { + BufferHandle m_VertexBuffer; + BufferHandle m_Materials; + }; + + ModelData modelData = { + .m_VertexBuffer = model.m_VertexPositionHandle, + .m_Materials = model.m_MaterialsHandle, + }; + INFO("Starting loop"); while (window.Poll()) { @@ -498,13 +513,23 @@ main(int, char **) cmd.setViewport(0, 1, &viewport); cmd.setScissor(0, 1, &scissor); cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, 0, sizeof *pushData, pushData); - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, sizeof *pushData, sizeof pvboBufferId, - &pvboBufferId); + // cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, 0, sizeof *pushData, pushData); + // cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, sizeof *pushData, sizeof pvboBufferId, + // &pvboBufferId); cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, &resourceManager.m_DescriptorSet, 0, nullptr); cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 1, 1, &descriptorSet, 0, nullptr); - cmd.draw(Cast(vertices.size()), 1, 0, 0); + // cmd.draw(Cast(vertices.size()), 1, 0, 0); + + cmd.bindIndexBuffer(model.m_IndexBuffer.m_Buffer, 0, vk::IndexType::eUint32); + cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, 0, sizeof modelData, &modelData); + + for (auto &prim : model.m_MeshPrimitives) + { + cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, sizeof modelData, + sizeof prim.m_MaterialIdx, &prim.m_MaterialIdx); + cmd.drawIndexed(prim.m_IndexCount, 1, prim.m_FirstIndex, Cast(prim.m_VertexOffset), 0); + } cmd.endRendering(); diff --git a/samples/03_model_render/pipeline_utils.h b/samples/03_model_render/pipeline_utils.h index 8b94fb0..a30754e 100644 --- a/samples/03_model_render/pipeline_utils.h +++ b/samples/03_model_render/pipeline_utils.h @@ -5,6 +5,7 @@ #pragma once +#include "global.h" #include "pipeline.h" #include diff --git a/samples/03_model_render/render_resource_manager.cpp b/samples/03_model_render/render_resource_manager.cpp index cfa195c..5797149 100644 --- a/samples/03_model_render/render_resource_manager.cpp +++ b/samples/03_model_render/render_resource_manager.cpp @@ -28,7 +28,7 @@ RenderResourceManager::WriteInfo::WriteInfo(vk::BufferView info) } BufferHandle -RenderResourceManager::Commit(const UniformStorageBuffer *storageBuffer) +RenderResourceManager::Commit(const StorageBuffer *storageBuffer) { const u32 handle = m_BufferFreeList.Alloc(); @@ -47,18 +47,53 @@ RenderResourceManager::Commit(const UniformStorageBuffer *storageBuffer) .pBufferInfo = &m_WriteInfos.back().uBufferInfo, }); + m_WriteOwner.emplace_back(HandleType::eBuffer, handle); + return {handle}; } +void +RenderResourceManager::EraseWrites(u32 handleIndex, HandleType handleType) +{ + auto writeIter = m_Writes.begin(); + auto ownerIter = m_WriteOwner.begin(); + const auto ownerEnd = m_WriteOwner.end(); + + while (ownerIter != ownerEnd) + { + if (ownerIter->first == handleType && ownerIter->second == handleIndex) + { + *writeIter = m_Writes.back(); + *ownerIter = m_WriteOwner.back(); + m_Writes.pop_back(); + m_WriteOwner.pop_back(); + return; + } + + ++ownerIter; + ++writeIter; + } +} + void RenderResourceManager::Release(BufferHandle handle) { + if (handle.IsInvalid()) + return; + + EraseWrites(handle.m_Index, HandleType::eBuffer); + m_BufferFreeList.Free(handle.m_Index); } void RenderResourceManager::Release(TextureHandle handle) { + if (handle.IsInvalid()) + return; + + EraseWrites(handle.m_Index, HandleType::eTexture); + m_TextureFreeList.Free(handle.m_Index); } @@ -82,13 +117,15 @@ RenderResourceManager::Commit(const Texture *texture) .pImageInfo = &m_WriteInfos.back().uImageInfo, }); + m_WriteOwner.emplace_back(HandleType::eBuffer, handle); + return {handle}; } void RenderResourceManager::Update() { - if (m_Writes.empty()) + if (m_Writes.empty() || m_WriteInfos.empty()) return; m_Device->m_Device.updateDescriptorSets(m_Writes.size(), m_Writes.data(), 0, nullptr); @@ -180,6 +217,10 @@ RenderResourceManager::RenderResourceManager(const Device *device, u16 maxSize) .pSetLayouts = &m_SetLayout, }; AbortIfFailed(device->m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &m_DescriptorSet)); + + m_Device->SetName(m_SetLayout, "Bindless Layout"); + m_Device->SetName(m_DescriptorPool, "Bindless Pool"); + m_Device->SetName(m_DescriptorSet, "Bindless Set"); } RenderResourceManager::~RenderResourceManager() diff --git a/samples/03_model_render/render_resource_manager.h b/samples/03_model_render/render_resource_manager.h index 79a59ed..94bf3a1 100644 --- a/samples/03_model_render/render_resource_manager.h +++ b/samples/03_model_render/render_resource_manager.h @@ -5,10 +5,12 @@ #pragma once +#include "buffer.h" #include "global.h" #include #include +#include struct Device; struct Texture; @@ -16,16 +18,23 @@ struct UniformStorageBuffer; struct RenderResourceManager; -struct Handle +struct GpuResourceHandle { - u32 m_Index; + constexpr static u32 INVALID_HANDLE = MaxValue; + u32 m_Index = INVALID_HANDLE; // Default = invalid + + [[nodiscard]] bool + IsInvalid() const + { + return m_Index == INVALID_HANDLE; + } }; -struct BufferHandle : Handle +struct BufferHandle : GpuResourceHandle { }; -struct TextureHandle : Handle +struct TextureHandle : GpuResourceHandle { }; @@ -38,6 +47,8 @@ struct FreeList void Init(u32 maxCapacity) { + // MaxValue is 'invalid-handle' so you can't use it as a handle. + assert(maxCapacity < GpuResourceHandle::INVALID_HANDLE); m_MaxCapacity = maxCapacity; } @@ -80,14 +91,25 @@ struct RenderResourceManager explicit WriteInfo(vk::BufferView info); }; + enum class HandleType + { + eBuffer, + eTexture, + }; + + using WriteOwner = eastl::pair; + eastl::deque m_WriteInfos; eastl::vector m_Writes; + eastl::vector m_WriteOwner; vk::Sampler m_Sampler; FreeList m_BufferFreeList; FreeList m_TextureFreeList; + void EraseWrites(u32 handleIndex, HandleType handleType); + public: const Device *m_Device; @@ -98,7 +120,7 @@ struct RenderResourceManager vk::DescriptorSetLayout m_SetLayout; vk::DescriptorSet m_DescriptorSet; - BufferHandle Commit(const UniformStorageBuffer *storageBuffer); + BufferHandle Commit(const StorageBuffer *storageBuffer); void Release(BufferHandle handle); TextureHandle Commit(const Texture *texture); void Release(TextureHandle handle); @@ -106,6 +128,8 @@ struct RenderResourceManager void Update(); // Ctor/Dtor - explicit RenderResourceManager(const Device *device, u16 maxSize); + RenderResourceManager(const Device *device, u16 maxSize); ~RenderResourceManager(); + + DISALLOW_COPY_AND_ASSIGN(RenderResourceManager); }; \ No newline at end of file diff --git a/samples/03_model_render/shader/model.frag.glsl b/samples/03_model_render/shader/model.frag.glsl index 1408b70..b60d837 100644 --- a/samples/03_model_render/shader/model.frag.glsl +++ b/samples/03_model_render/shader/model.frag.glsl @@ -2,16 +2,45 @@ #pragma shader_stage(fragment) #extension GL_EXT_nonuniform_qualifier : enable -layout (location = 0) in vec2 inUV; +//layout (location = 0) in vec2 inUV; layout (location = 0) out vec4 outColor; -layout(set = 0, binding = 1) uniform sampler2D textures[]; - -layout(push_constant) uniform Block { - uint textureHandle; - uint vertexBufferHandle; +struct VertexData { + float position[4]; +// float uv[2]; }; +struct MaterialData { + float m_AlbedoFactor[4]; + float m_EmissionFactor[4]; + float m_MetalFactor; + float m_RoughFactor; + uint m_AlbedoTex; + uint m_NormalTex; + uint m_MetalRoughTex; + uint m_OcclusionTex; + uint m_EmissionTex; +}; + +layout(set = 0, binding = 1) uniform sampler2D textures[]; +layout(std430, set = 0, binding = 0) readonly buffer Vertices { + VertexData data[]; +} vertexBuffer[]; +layout(std430, set = 0, binding = 0) readonly buffer Materials { + MaterialData data[]; +} materialsBuffer[]; + + +layout(push_constant) uniform Block { + uint vertexBufferHandle; + uint materialBufferHandle; + uint materialIdx; +}; + +vec4 ToVec4(float array[4]) { + return vec4(array[0],array[1],array[2],array[3]); +} + void main() { - outColor = vec4(texture(textures[textureHandle], inUV).rgb, 1.0f); + outColor = ToVec4(materialsBuffer[materialBufferHandle].data[materialIdx].m_AlbedoFactor); // vec4(texture(textures[textureHandle], inUV).rgb, 1.0f); } \ No newline at end of file diff --git a/samples/03_model_render/shader/model.vert.glsl b/samples/03_model_render/shader/model.vert.glsl index fbed2df..52fda5e 100644 --- a/samples/03_model_render/shader/model.vert.glsl +++ b/samples/03_model_render/shader/model.vert.glsl @@ -2,16 +2,31 @@ #pragma shader_stage(vertex) #extension GL_EXT_nonuniform_qualifier : enable -layout(location = 0) out vec2 outUV; +//layout(location = 0) out vec2 outUV; struct VertexData { - float position[3]; - float uv[2]; + float position[4]; +// float uv[2]; +}; + +struct MaterialData { + float m_AlbedoFactor[4]; + float m_EmissionFactor[4]; + float m_MetalFactor; + float m_RoughFactor; + uint m_AlbedoTex; + uint m_NormalTex; + uint m_MetalRoughTex; + uint m_OcclusionTex; + uint m_EmissionTex; }; layout(std430, set = 0, binding = 0) readonly buffer Vertices { VertexData data[]; } vertexBuffer[]; +layout(std430, set = 0, binding = 0) readonly buffer Materials { + MaterialData data[]; +} materialsBuffer[]; vec3 GetPosition(uint bufferId, uint vertexIdx) { return vec3( @@ -21,12 +36,12 @@ vec3 GetPosition(uint bufferId, uint vertexIdx) { ); } -vec2 GetUV(uint bufferId, uint vertexIdx) { - return vec2( - vertexBuffer[bufferId].data[vertexIdx].uv[0], - vertexBuffer[bufferId].data[vertexIdx].uv[1] - ); -} +//vec2 GetUV(uint bufferId, uint vertexIdx) { +// return vec2( +// vertexBuffer[bufferId].data[vertexIdx].uv[0], +// vertexBuffer[bufferId].data[vertexIdx].uv[1] +// ); +//} layout(set = 1, binding = 0) uniform Camera { mat4 model; @@ -35,11 +50,12 @@ layout(set = 1, binding = 0) uniform Camera { } ubo; layout(push_constant) uniform Block { - uint textureHandle; uint vertexBufferHandle; + uint materialBufferHandle; + uint materialIdx; }; void main() { - outUV = GetUV(vertexBufferHandle, gl_VertexIndex); +// outUV = GetUV(vertexBufferHandle, gl_VertexIndex); gl_Position = ubo.proj * ubo.view * ubo.model * vec4(GetPosition(vertexBufferHandle, gl_VertexIndex), 1.0f); } \ No newline at end of file