diff --git a/aster/buffer.cpp b/aster/buffer.cpp index 74396f3..4564cca 100644 --- a/aster/buffer.cpp +++ b/aster/buffer.cpp @@ -11,11 +11,11 @@ void Buffer::Destroy(const Device *device) { - if (!m_Buffer) + if (!IsValid()) return; vmaDestroyBuffer(device->m_Allocator, m_Buffer, m_Allocation); - m_Buffer = nullptr; + m_Size &= ~VALID_BUFFER_BIT; } void @@ -41,24 +41,21 @@ Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUs &allocationCreateInfo, &buffer, &allocation, &allocationInfo)); ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result); - m_Buffer = buffer; - m_Size = size & SIZE_MASK; - m_Allocation = allocation; - m_Mapped = allocationInfo.pMappedData; - vk::MemoryPropertyFlags memoryPropertyFlags; vmaGetAllocationMemoryProperties(device->m_Allocator, allocation, Recast(&memoryPropertyFlags)); - if (memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible) - { - m_Size |= HOST_ACCESSIBLE_BIT; - } + bool hostAccessible = Cast(memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible); + + m_Buffer = buffer; + m_Size = size | VALID_BUFFER_BIT | (hostAccessible ? HOST_ACCESSIBLE_BIT : 0); + m_Allocation = allocation; + m_Mapped = allocationInfo.pMappedData; device->SetName(m_Buffer, name); } void -Buffer::Write(const Device *device, usize offset, usize size, void *data) +Buffer::Write(const Device *device, usize offset, usize size, const void *data) { assert(IsHostVisible()); @@ -93,6 +90,15 @@ UniformBuffer::Init(const Device *device, const usize size, const cstr name) VMA_MEMORY_USAGE_AUTO, name); } +void +UniformStorageBuffer::Init(const Device *device, usize size, 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); +} + void VertexBuffer::Init(const Device *device, usize size, cstr name) { diff --git a/aster/buffer.h b/aster/buffer.h index 401f86b..6ef3030 100644 --- a/aster/buffer.h +++ b/aster/buffer.h @@ -17,19 +17,23 @@ struct Buffer [[nodiscard]] usize GetSize() const; [[nodiscard]] bool IsHostVisible() const; + [[nodiscard]] bool IsValid() const; [[nodiscard]] bool IsMapped() const; void Destroy(const Device *device); - void Write(const Device *device, usize offset, usize size, void *data); + void Write(const Device *device, usize offset, usize size, const void *data); protected: void Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage, VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name); + // Buffer size is used intrusively by the Render Resource Manager + // If the buffer is Invalid, the remaining data in Buffer is used for other tasks. usize m_Size = 0; - constexpr static usize HOST_ACCESSIBLE_BIT = 1llu << 63; - constexpr static usize SIZE_MASK = ~HOST_ACCESSIBLE_BIT; + constexpr static usize VALID_BUFFER_BIT = Cast(1llu << 63); + constexpr static usize HOST_ACCESSIBLE_BIT = 1llu << 62; + constexpr static usize SIZE_MASK = ~(VALID_BUFFER_BIT | HOST_ACCESSIBLE_BIT); }; struct UniformBuffer : Buffer @@ -37,6 +41,11 @@ struct UniformBuffer : Buffer void Init(const Device *device, usize size, cstr name = nullptr); }; +struct UniformStorageBuffer : Buffer +{ + void Init(const Device *device, usize size, cstr name = nullptr); +}; + struct VertexBuffer : Buffer { void Init(const Device *device, usize size, cstr name = nullptr); @@ -60,6 +69,12 @@ Buffer::IsHostVisible() const return m_Size & HOST_ACCESSIBLE_BIT; } +inline bool +Buffer::IsValid() const +{ + return m_Size & VALID_BUFFER_BIT; +} + inline bool Buffer::IsMapped() const { diff --git a/aster/constants.h b/aster/constants.h index 8ee2b70..0ba78fd 100644 --- a/aster/constants.h +++ b/aster/constants.h @@ -27,7 +27,7 @@ using f128 = long double; using b8 = bool; using b32 = u32; using usize = size_t; -using p64 = intptr_t; +using uptr = uintptr_t; using cstr = const char *; namespace ansi_color diff --git a/aster/global.h b/aster/global.h index 040514a..18394fd 100644 --- a/aster/global.h +++ b/aster/global.h @@ -75,7 +75,7 @@ HashCombine(const usize hash0, const usize hash1) struct Time { - static constexpr f64 cMaxDelta = 0.1; + static constexpr f64 MAX_DELTA = 0.1; inline static f64 m_Elapsed{Qnan}; inline static f64 m_Delta{Qnan}; @@ -93,7 +93,7 @@ struct Time { ERROR_IF(std::isnan(m_Elapsed), "Time not init."); const auto newElapsed = glfwGetTime(); - m_Delta = std::clamp(newElapsed - m_Elapsed, 0.0, cMaxDelta); + m_Delta = std::clamp(newElapsed - m_Elapsed, 0.0, MAX_DELTA); m_Elapsed = newElapsed; } }; diff --git a/aster/image.cpp b/aster/image.cpp index b4d1deb..b6f8c3e 100644 --- a/aster/image.cpp +++ b/aster/image.cpp @@ -10,9 +10,10 @@ void Image::Destroy(const Device *device) { - if (!m_Image) + if (!IsValid()) return; + device->m_Device.destroy(m_View, nullptr); vmaDestroyImage(device->m_Allocator, m_Image, m_Allocation); m_Image = nullptr; } @@ -20,10 +21,12 @@ Image::Destroy(const Device *device) void Texture::Init(const Device *device, const vk::Extent2D extent, const bool isMipmapped, const cstr name) { + constexpr vk::Format imageFormat = vk::Format::eR8G8B8A8Srgb; + const u32 mipLevels = isMipmapped ? 1 + Cast(floor(log2(eastl::max(extent.width, extent.height)))) : 1; vk::ImageCreateInfo imageCreateInfo = { .imageType = vk::ImageType::e2D, - .format = vk::Format::eR8G8B8A8Srgb, + .format = imageFormat, .extent = {.width = extent.width, .height = extent.height, .depth = 1}, .mipLevels = mipLevels, .arrayLayers = 1, @@ -42,9 +45,28 @@ Texture::Init(const Device *device, const vk::Extent2D extent, const bool isMipm VmaAllocation allocation; auto result = Cast(vmaCreateImage(device->m_Allocator, Recast(&imageCreateInfo), &allocationCreateInfo, &image, &allocation, nullptr)); - ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result); + ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result); + + vk::ImageView view; + vk::ImageViewCreateInfo imageViewCreateInfo = { + .image = image, + .viewType = vk::ImageViewType::e2D, + .format = imageFormat, + .components = {}, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = mipLevels, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view); + ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result); m_Image = image; + m_View = view; m_Allocation = allocation; m_Extent = {extent.width, extent.height, 1}; @@ -54,9 +76,10 @@ Texture::Init(const Device *device, const vk::Extent2D extent, const bool isMipm void DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name) { + constexpr vk::Format imageFormat = vk::Format::eD32Sfloat; vk::ImageCreateInfo imageCreateInfo = { .imageType = vk::ImageType::e2D, - .format = vk::Format::eD32Sfloat, + .format = imageFormat, .extent = {.width = extent.width, .height = extent.height, .depth = 1}, .mipLevels = 1, .arrayLayers = 1, @@ -75,9 +98,28 @@ DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name) VmaAllocation allocation; auto result = Cast(vmaCreateImage(device->m_Allocator, Recast(&imageCreateInfo), &allocationCreateInfo, &image, &allocation, nullptr)); - ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result); + ERROR_IF(Failed(result), "Could not allocate depth buffer. Cause: {}", result) THEN_ABORT(result); + + vk::ImageView view; + vk::ImageViewCreateInfo imageViewCreateInfo = { + .image = image, + .viewType = vk::ImageViewType::e2D, + .format = imageFormat, + .components = {}, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eDepth, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view); + ERROR_IF(Failed(result), "Could not create depth image view {}. Cause: {}", name, result) THEN_ABORT(result); m_Image = image; + m_View = view; m_Allocation = allocation; m_Extent = {extent.width, extent.height, 1}; diff --git a/aster/image.h b/aster/image.h index 3d6fb39..74d2707 100644 --- a/aster/image.h +++ b/aster/image.h @@ -12,9 +12,11 @@ struct Device; struct Image { vk::Image m_Image = nullptr; + vk::ImageView m_View = nullptr; VmaAllocation m_Allocation = nullptr; vk::Extent3D m_Extent; - usize m_Size = 0; + + [[nodiscard]] bool IsValid() const; void Destroy(const Device *device); }; @@ -27,4 +29,10 @@ struct Texture : Image struct DepthImage : Image { void Init(const Device *device, vk::Extent2D extent, cstr name = nullptr); -}; \ No newline at end of file +}; + +inline bool +Image::IsValid() const +{ + return m_Image; +} \ No newline at end of file diff --git a/samples/02_box/box.cpp b/samples/02_box/box.cpp index a4148a7..87cf3d7 100644 --- a/samples/02_box/box.cpp +++ b/samples/02_box/box.cpp @@ -60,7 +60,7 @@ ImageFile::Load(cstr fileName) usize ImageFile::GetSize() const { - return m_Width * m_Height * m_NumChannels; + return Cast(m_Width) * m_Height * m_NumChannels; } ImageFile::~ImageFile() @@ -330,30 +330,8 @@ main(int, char **) imageStaging.Destroy(&device); } - vk::ImageView imageView; vk::Sampler sampler; { - vk::ImageViewCreateInfo imageViewCreateInfo = { - .image = crate.m_Image, - .viewType = vk::ImageViewType::e2D, - .format = vk::Format::eR8G8B8A8Srgb, - .components = - vk::ComponentMapping{ - .r = vk::ComponentSwizzle::eIdentity, - .g = vk::ComponentSwizzle::eIdentity, - .b = vk::ComponentSwizzle::eIdentity, - .a = vk::ComponentSwizzle::eIdentity, - }, - .subresourceRange = - { - .aspectMask = vk::ImageAspectFlagBits::eColor, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - AbortIfFailed(device.m_Device.createImageView(&imageViewCreateInfo, nullptr, &imageView)); vk::SamplerCreateInfo samplerCreateInfo = { .magFilter = vk::Filter::eLinear, .minFilter = vk::Filter::eLinear, @@ -382,7 +360,7 @@ main(int, char **) }; vk::DescriptorImageInfo descriptorImageInfo = { .sampler = sampler, - .imageView = imageView, + .imageView = crate.m_View, .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, }; eastl::array writeDescriptors = { @@ -452,52 +430,16 @@ main(int, char **) FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT}; eastl::fixed_vector depthImages(frameManager.m_FramesInFlight); - eastl::fixed_vector depthViews(frameManager.m_FramesInFlight); - - vk::ImageSubresourceRange depthSubresourceRange = { - .aspectMask = vk::ImageAspectFlagBits::eDepth, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }; - - u32 index = 0; for (auto &depthImage : depthImages) { - auto name = fmt::format("Depth image {}", index); - depthImage.Init(&device, swapchain.m_Extent, name.c_str()); - vk::ImageViewCreateInfo imageViewCreateInfo = { - .image = depthImage.m_Image, - .viewType = vk::ImageViewType::e2D, - .format = vk::Format::eD32Sfloat, - .components = vk::ComponentMapping{.r = vk::ComponentSwizzle::eIdentity}, - .subresourceRange = depthSubresourceRange, - }; - AbortIfFailed(device.m_Device.createImageView(&imageViewCreateInfo, nullptr, &depthViews[index])); - - index++; + depthImage.Init(&device, swapchain.m_Extent, "Depth"); } - auto recreateDepthBuffers = [&device, &depthImages, &depthViews, depthSubresourceRange](vk::Extent2D extent) { - for (auto &depthView : depthViews) - { - device.m_Device.destroy(depthView, nullptr); - } - u32 index = 0; + auto recreateDepthBuffers = [&device, &depthImages](vk::Extent2D extent) { for (auto &depthImage : depthImages) { depthImage.Destroy(&device); depthImage.Init(&device, extent, "Depth"); - vk::ImageViewCreateInfo imageViewCreateInfo = { - .image = depthImage.m_Image, - .viewType = vk::ImageViewType::e2D, - .format = vk::Format::eD32Sfloat, - .components = vk::ComponentMapping{.r = vk::ComponentSwizzle::eIdentity}, - .subresourceRange = depthSubresourceRange, - }; - AbortIfFailed(device.m_Device.createImageView(&imageViewCreateInfo, nullptr, &depthViews[index])); - ++index; } }; swapchain.RegisterResizeCallback(recreateDepthBuffers); @@ -518,7 +460,7 @@ main(int, char **) vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex]; vk::Image currentImage = swapchain.m_Images[imageIndex]; vk::CommandBuffer cmd = currentFrame->m_CommandBuffer; - vk::ImageView currentDepthImageView = depthViews[currentFrame->m_FrameIdx]; + vk::ImageView currentDepthImageView = depthImages[currentFrame->m_FrameIdx].m_View; topOfThePipeBarrier.image = currentImage; renderToPresentBarrier.image = currentImage; @@ -592,16 +534,11 @@ main(int, char **) AbortIfFailed(device.m_Device.waitIdle()); - for (auto &depthView : depthViews) - { - device.m_Device.destroy(depthView, nullptr); - } for (auto &depthImage : depthImages) { depthImage.Destroy(&device); } device.m_Device.destroy(sampler, nullptr); - device.m_Device.destroy(imageView, nullptr); ubo.Destroy(&device); device.m_Device.destroy(descriptorPool, nullptr); device.m_Device.destroy(copyPool, nullptr); diff --git a/samples/03_model_render/CMakeLists.txt b/samples/03_model_render/CMakeLists.txt index a6c718a..8753a51 100644 --- a/samples/03_model_render/CMakeLists.txt +++ b/samples/03_model_render/CMakeLists.txt @@ -18,4 +18,5 @@ target_link_libraries(model_render PRIVATE util_helper) target_include_directories(model_render PRIVATE ${TINYGLTF_INCLUDE_DIRS}) -add_resource_dir(model_render model) \ No newline at end of file +add_resource_dir(model_render model) +add_resource_dir(model_render image) diff --git a/samples/03_model_render/image/container.jpg b/samples/03_model_render/image/container.jpg new file mode 100644 index 0000000..f04776f --- /dev/null +++ b/samples/03_model_render/image/container.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:126baccff187648acfad78c57560035f5c783cc6ec92a37a93a5b1edc97af10e +size 184939 diff --git a/samples/03_model_render/model_render.cpp b/samples/03_model_render/model_render.cpp index 4c7979c..e91010c 100644 --- a/samples/03_model_render/model_render.cpp +++ b/samples/03_model_render/model_render.cpp @@ -36,11 +36,13 @@ constexpr auto MODEL_FILE = "model/Box.glb"; struct ImageFile { - u8 *m_Data = nullptr; - u32 m_Width = 512; - u32 m_Height = 512; - u32 m_NumChannels = 4; + void *m_Data = nullptr; + u32 m_Width = 0; + u32 m_Height = 0; + u32 m_NumChannels = 0; + bool m_Constant = false; + bool Load(cstr fileName); bool Load(vec4 color); [[nodiscard]] usize GetSize() const; ~ImageFile(); @@ -102,14 +104,27 @@ main(int, char **) Features enabledDeviceFeatures = { .m_Vulkan10Features = {.samplerAnisotropy = true}, - .m_Vulkan12Features = {.descriptorIndexing = true}, + .m_Vulkan12Features = + { + .descriptorIndexing = true, + .shaderSampledImageArrayNonUniformIndexing = true, + .shaderStorageBufferArrayNonUniformIndexing = true, + .shaderStorageImageArrayNonUniformIndexing = true, + .descriptorBindingUniformBufferUpdateAfterBind = true, // Not related to Bindless + .descriptorBindingSampledImageUpdateAfterBind = true, + .descriptorBindingStorageImageUpdateAfterBind = true, + .descriptorBindingStorageBufferUpdateAfterBind = true, + .descriptorBindingPartiallyBound = true, + .runtimeDescriptorArray = true, + }, .m_Vulkan13Features = {.dynamicRendering = true}, }; + QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse); 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); + RenderResourceManager resourceManager(&device, 10); Pipeline pipeline = CreatePipeline(&device, &swapchain, &resourceManager); @@ -210,55 +225,97 @@ main(int, char **) Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)}, }; - ImageFile imageFile; - bool loaded = imageFile.Load({0.7f, 0.4f, 0.1f, 1.0f}); - assert(loaded); - INFO("Image {}x{} : {} channels", imageFile.m_Width, imageFile.m_Height, imageFile.m_NumChannels); + ImageFile crateImageFile; + ImageFile plainImageFile; + assert(crateImageFile.Load("image/container.jpg")); + INFO("Image {}x{} : {} channels", crateImageFile.m_Width, crateImageFile.m_Height, crateImageFile.m_NumChannels); + assert(plainImageFile.Load({0.7f, 0.4f, 0.1f, 1.0f})); VertexBuffer vbo; - Texture crate; + Texture crateTexture; + Texture plainTexture; vbo.Init(&device, vertices.size() * sizeof vertices[0], "VBO"); - crate.Init(&device, {imageFile.m_Width, imageFile.m_Height}, false, "Crate Texture"); + crateTexture.Init(&device, {crateImageFile.m_Width, crateImageFile.m_Height}, false, "Crate Texture"); + plainTexture.Init(&device, {crateImageFile.m_Width, crateImageFile.m_Height}, false, "Plain Texture"); + + auto crateTextureId = resourceManager.Commit(&crateTexture); + auto plainTextureId = resourceManager.Commit(&plainTexture); { - StagingBuffer vertexStaging, imageStaging; + StagingBuffer vertexStaging, imageStaging1, imageStaging2; vertexStaging.Init(&device, vertices.size() * sizeof vertices[0], "Vertex Staging"); vertexStaging.Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data()); - imageStaging.Init(&device, imageFile.GetSize(), "Image Staging"); - INFO("fine {}", imageFile.GetSize()); - imageStaging.Write(&device, 0, imageFile.GetSize(), imageFile.m_Data); + imageStaging1.Init(&device, crateImageFile.GetSize(), "Image Staging 1"); + imageStaging1.Write(&device, 0, crateImageFile.GetSize(), crateImageFile.m_Data); - vk::ImageMemoryBarrier imageReadyToWrite = { - .oldLayout = vk::ImageLayout::eUndefined, - .newLayout = vk::ImageLayout::eTransferDstOptimal, - .srcQueueFamilyIndex = queueAllocation.m_Family, - .dstQueueFamilyIndex = queueAllocation.m_Family, - .image = crate.m_Image, - .subresourceRange = - { - .aspectMask = vk::ImageAspectFlagBits::eColor, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, + imageStaging2.Init(&device, plainImageFile.GetSize(), "Image Staging 2"); + imageStaging2.Write(&device, 0, plainImageFile.GetSize(), plainImageFile.m_Data); + + eastl::array imageReadyToWrite = { + vk::ImageMemoryBarrier{ + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eTransferDstOptimal, + .srcQueueFamilyIndex = queueAllocation.m_Family, + .dstQueueFamilyIndex = queueAllocation.m_Family, + .image = crateTexture.m_Image, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }, + vk::ImageMemoryBarrier{ + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eTransferDstOptimal, + .srcQueueFamilyIndex = queueAllocation.m_Family, + .dstQueueFamilyIndex = queueAllocation.m_Family, + .image = plainTexture.m_Image, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }, }; - vk::ImageMemoryBarrier imageReadyToRead = { - .oldLayout = vk::ImageLayout::eTransferDstOptimal, - .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, - .srcQueueFamilyIndex = queueAllocation.m_Family, - .dstQueueFamilyIndex = queueAllocation.m_Family, - .image = crate.m_Image, - .subresourceRange = - { - .aspectMask = vk::ImageAspectFlagBits::eColor, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, + eastl::array imageReadyToRead = { + vk::ImageMemoryBarrier{ + .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + .srcQueueFamilyIndex = queueAllocation.m_Family, + .dstQueueFamilyIndex = queueAllocation.m_Family, + .image = crateTexture.m_Image, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }, + vk::ImageMemoryBarrier{ + .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + .srcQueueFamilyIndex = queueAllocation.m_Family, + .dstQueueFamilyIndex = queueAllocation.m_Family, + .image = plainTexture.m_Image, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }, }; vk::Fence fence; @@ -268,15 +325,15 @@ main(int, char **) vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; AbortIfFailed(copyBuffer.begin(&beginInfo)); copyBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eHost, vk::PipelineStageFlagBits::eTransfer, {}, 0, - nullptr, 0, nullptr, 1, &imageReadyToWrite); + nullptr, 0, nullptr, Cast(imageReadyToWrite.size()), imageReadyToWrite.data()); vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = vertexStaging.GetSize()}; copyBuffer.copyBuffer(vertexStaging.m_Buffer, vbo.m_Buffer, 1, &bufferCopy); vk::BufferImageCopy imageCopy = { .bufferOffset = 0, - .bufferRowLength = imageFile.m_Width, - .bufferImageHeight = imageFile.m_Height, + .bufferRowLength = crateImageFile.m_Width, + .bufferImageHeight = crateImageFile.m_Height, .imageSubresource = { .aspectMask = vk::ImageAspectFlagBits::eColor, @@ -285,13 +342,15 @@ main(int, char **) .layerCount = 1, }, .imageOffset = {}, - .imageExtent = {imageFile.m_Width, imageFile.m_Height, 1}, + .imageExtent = {crateImageFile.m_Width, crateImageFile.m_Height, 1}, }; - copyBuffer.copyBufferToImage(imageStaging.m_Buffer, crate.m_Image, vk::ImageLayout::eTransferDstOptimal, 1, - &imageCopy); + copyBuffer.copyBufferToImage(imageStaging1.m_Buffer, crateTexture.m_Image, vk::ImageLayout::eTransferDstOptimal, + 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, 1, &imageReadyToRead); + 0, nullptr, 0, nullptr, Cast(imageReadyToRead.size()), imageReadyToRead.data()); AbortIfFailed(copyBuffer.end()); @@ -310,49 +369,8 @@ main(int, char **) device.m_Device.destroy(fence, nullptr); vertexStaging.Destroy(&device); - imageStaging.Destroy(&device); - } - - vk::ImageView imageView; - vk::Sampler sampler; - { - vk::ImageViewCreateInfo imageViewCreateInfo = { - .image = crate.m_Image, - .viewType = vk::ImageViewType::e2D, - .format = vk::Format::eR8G8B8A8Srgb, - .components = - vk::ComponentMapping{ - .r = vk::ComponentSwizzle::eIdentity, - .g = vk::ComponentSwizzle::eIdentity, - .b = vk::ComponentSwizzle::eIdentity, - .a = vk::ComponentSwizzle::eIdentity, - }, - .subresourceRange = - { - .aspectMask = vk::ImageAspectFlagBits::eColor, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - AbortIfFailed(device.m_Device.createImageView(&imageViewCreateInfo, nullptr, &imageView)); - vk::SamplerCreateInfo samplerCreateInfo = { - .magFilter = vk::Filter::eLinear, - .minFilter = vk::Filter::eLinear, - .mipmapMode = vk::SamplerMipmapMode::eLinear, - .addressModeU = vk::SamplerAddressMode::eRepeat, - .addressModeV = vk::SamplerAddressMode::eRepeat, - .addressModeW = vk::SamplerAddressMode::eRepeat, - .mipLodBias = 0.2, - .anisotropyEnable = true, - .maxAnisotropy = 1.0f, - .compareEnable = false, - .minLod = 0, - .maxLod = 4, - .unnormalizedCoordinates = false, - }; - AbortIfFailed(device.m_Device.createSampler(&samplerCreateInfo, nullptr, &sampler)); + imageStaging1.Destroy(&device); + imageStaging2.Destroy(&device); } UniformBuffer ubo; @@ -363,11 +381,6 @@ main(int, char **) .offset = 0, .range = ubo.GetSize(), }; - vk::DescriptorImageInfo descriptorImageInfo = { - .sampler = sampler, - .imageView = imageView, - .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, - }; eastl::array writeDescriptors = { vk::WriteDescriptorSet{ .dstSet = descriptorSet, @@ -377,17 +390,11 @@ main(int, char **) .descriptorType = vk::DescriptorType::eUniformBuffer, .pBufferInfo = &descriptorBufferInfo, }, - vk::WriteDescriptorSet{ - .dstSet = descriptorSet, - .dstBinding = 1, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - .pImageInfo = &descriptorImageInfo, - }, }; device.m_Device.updateDescriptorSets(Cast(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr); + resourceManager.Update(); + // Persistent variables vk::Viewport viewport = { .x = 0, @@ -487,11 +494,27 @@ main(int, char **) Time::Init(); + Handle *pushData = &crateTextureId; + Handle *otherPushData = &plainTextureId; + + bool prevPressed = false; + auto isSpaceJustPressed = [&prevPressed, &window] { + bool pressed = glfwGetKey(window.m_Window, GLFW_KEY_SPACE) == GLFW_PRESS; + bool justPressed = pressed & !prevPressed; + prevPressed = pressed; + return justPressed; + }; + INFO("Starting loop"); while (window.Poll()) { Time::Update(); + if (isSpaceJustPressed()) + { + eastl::swap(pushData, otherPushData); + } + camera.m_Model *= rotate(mat4{1.0f}, Cast(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f)); ubo.Write(&device, 0, sizeof camera, &camera); @@ -546,6 +569,9 @@ 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.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); usize offsets = 0; cmd.bindVertexBuffers(0, 1, &vbo.m_Buffer, &offsets); @@ -583,12 +609,11 @@ main(int, char **) { depthImage.Destroy(&device); } - device.m_Device.destroy(sampler, nullptr); - device.m_Device.destroy(imageView, nullptr); ubo.Destroy(&device); device.m_Device.destroy(descriptorPool, nullptr); device.m_Device.destroy(copyPool, nullptr); - crate.Destroy(&device); + crateTexture.Destroy(&device); + plainTexture.Destroy(&device); vbo.Destroy(&device); return 0; @@ -598,27 +623,60 @@ bool ImageFile::Load(vec4 color) { constexpr usize size = 512llu * 512llu * 4llu; - m_Data = new u8[size]; + u8 *pData = new u8[size]; vec4 color255 = 255.999f * color; glm::vec<4, u8> color8 = color255; for (usize i = 0; i < size; i += 4) { - memcpy(m_Data + i, &color8, sizeof color8); + memcpy(pData + i, &color8, sizeof color8); } + m_Data = pData; + m_Constant = true; + m_Height = 512; + m_Width = 512; + m_NumChannels = 4; + + return true; +} + +bool +ImageFile::Load(cstr fileName) +{ + int width, height, nrChannels; + m_Data = stbi_load(fileName, &width, &height, &nrChannels, 4); + ERROR_IF(!m_Data, "Could not load {}", fileName); + + if (!m_Data) + { + return false; + } + + m_Width = width; + m_Height = height; + m_NumChannels = 4; + m_Constant = false; + return true; } usize ImageFile::GetSize() const { - return m_Width * m_Height * m_NumChannels; + return Cast(m_Width) * m_Height * m_NumChannels; } ImageFile::~ImageFile() { - delete[] m_Data; + if (m_Constant) + { + delete[] Cast(m_Data); + } + else + { + stbi_image_free(m_Data); + } m_Data = nullptr; -} \ No newline at end of file +} diff --git a/samples/03_model_render/pipeline_utils.cpp b/samples/03_model_render/pipeline_utils.cpp index aff8543..094c72d 100644 --- a/samples/03_model_render/pipeline_utils.cpp +++ b/samples/03_model_render/pipeline_utils.cpp @@ -44,12 +44,6 @@ CreatePipeline(const Device *device, const Swapchain *swapchain, const RenderRes .descriptorCount = 1, .stageFlags = vk::ShaderStageFlagBits::eVertex, }, - vk::DescriptorSetLayoutBinding{ - .binding = 1, - .descriptorType = vk::DescriptorType::eCombinedImageSampler, - .descriptorCount = 1, - .stageFlags = vk::ShaderStageFlagBits::eFragment, - }, }; vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { .bindingCount = Cast(descriptorSetLayoutBindings.size()), @@ -61,11 +55,17 @@ CreatePipeline(const Device *device, const Swapchain *swapchain, const RenderRes descriptorSetLayouts.push_back(descriptorSetLayout); } + vk::PushConstantRange pushConstantRange = { + .stageFlags = vk::ShaderStageFlagBits::eAll, + .offset = 0, + .size = 16, + }; + vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { .setLayoutCount = Cast(descriptorSetLayouts.size()), .pSetLayouts = descriptorSetLayouts.data(), - .pushConstantRangeCount = 0, - .pPushConstantRanges = nullptr, + .pushConstantRangeCount = 1, + .pPushConstantRanges = &pushConstantRange, }; vk::PipelineLayout pipelineLayout; AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); diff --git a/samples/03_model_render/render_resource_manager.cpp b/samples/03_model_render/render_resource_manager.cpp index 6d2e4e7..ebee2db 100644 --- a/samples/03_model_render/render_resource_manager.cpp +++ b/samples/03_model_render/render_resource_manager.cpp @@ -10,45 +10,148 @@ #include "helpers.h" #include "image.h" -RenderResourceManager::RenderResourceManager(const Device *device) +#include + +RenderResourceManager::WriteInfo::WriteInfo(vk::DescriptorBufferInfo info) + : uBufferInfo(info) +{ +} + +RenderResourceManager::WriteInfo::WriteInfo(vk::DescriptorImageInfo info) + : uImageInfo(info) +{ +} + +RenderResourceManager::WriteInfo::WriteInfo(vk::BufferView info) + : uBufferView(info) +{ +} + +BufferHandle +RenderResourceManager::Commit(const UniformStorageBuffer *storageBuffer) +{ + const u32 handle = m_BufferFreeList.Alloc(); + + m_WriteInfos.emplace_back(vk::DescriptorBufferInfo{ + .buffer = storageBuffer->m_Buffer, + .offset = 0, + .range = storageBuffer->GetSize(), + }); + + m_Writes.push_back({ + .dstSet = m_DescriptorSet, + .dstBinding = BUFFER_BINDING_INDEX, + .dstArrayElement = handle, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eStorageBuffer, + .pBufferInfo = &m_WriteInfos.back().uBufferInfo, + }); + + return {handle}; +} + +TextureHandle +RenderResourceManager::Commit(const Texture *texture) +{ + const u32 handle = m_TextureFreeList.Alloc(); + + m_WriteInfos.emplace_back(vk::DescriptorImageInfo{ + .sampler = nullptr, + .imageView = texture->m_View, + .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + }); + + m_Writes.push_back({ + .dstSet = m_DescriptorSet, + .dstBinding = TEXTURE_BINDING_INDEX, + .dstArrayElement = handle, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &m_WriteInfos.back().uImageInfo, + }); + + return {handle}; +} + +void +RenderResourceManager::Update() +{ + usize count = Cast(m_Writes.size()); + vk::WriteDescriptorSet *pData = m_Writes.data(); + if (count > 0) + { + m_Device->m_Device.updateDescriptorSets(count, pData, 0, nullptr); + } + + m_Writes.clear(); + m_WriteInfos.clear(); +} + +RenderResourceManager::RenderResourceManager(const Device *device, u16 maxSize) : m_Device(device) { vk::PhysicalDeviceProperties properties; m_Device->m_PhysicalDevice.getProperties(&properties); - auto maxBufferCount = properties.limits.maxDescriptorSetStorageBuffers - 1024; - auto maxTextureCount = properties.limits.maxDescriptorSetSampledImages - 1024; + u32 buffersCount = eastl::min(properties.limits.maxPerStageDescriptorStorageBuffers - 1024, Cast(maxSize)); + u32 texturesCount = eastl::min(properties.limits.maxPerStageDescriptorSampledImages - 1024, Cast(maxSize)); - INFO("Max Buffer Count: {}", maxBufferCount); - INFO("Max Texture Count: {}", maxTextureCount); + vk::SamplerCreateInfo samplerCreateInfo = { + .magFilter = vk::Filter::eLinear, + .minFilter = vk::Filter::eLinear, + .mipmapMode = vk::SamplerMipmapMode::eLinear, + .addressModeU = vk::SamplerAddressMode::eClampToBorder, + .addressModeV = vk::SamplerAddressMode::eClampToBorder, + .addressModeW = vk::SamplerAddressMode::eClampToBorder, + .mipLodBias = 0.0f, + .anisotropyEnable = true, + .maxAnisotropy = properties.limits.maxSamplerAnisotropy, + .compareEnable = false, + .minLod = 0, + .maxLod = vk::LodClampNone, + .borderColor = vk::BorderColor::eFloatOpaqueBlack, + .unnormalizedCoordinates = false, + }; + AbortIfFailed(device->m_Device.createSampler(&samplerCreateInfo, nullptr, &m_Sampler)); - m_Buffers.resize(maxBufferCount); - m_Textures.resize(maxTextureCount); + INFO("Max Buffer Count: {}", buffersCount); + INFO("Max Texture Count: {}", texturesCount); + + m_BufferFreeList.Init(buffersCount); + m_TextureFreeList.Init(texturesCount); eastl::array poolSizes = { - vk::DescriptorPoolSize{.type = vk::DescriptorType::eStorageBuffer, .descriptorCount = maxBufferCount}, - vk::DescriptorPoolSize{.type = vk::DescriptorType::eSampledImage, .descriptorCount = maxTextureCount}, + vk::DescriptorPoolSize{ + .type = vk::DescriptorType::eStorageBuffer, + .descriptorCount = buffersCount, + }, + vk::DescriptorPoolSize{ + .type = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = texturesCount, + }, }; - vk::DescriptorPoolCreateInfo poolCreateInfo = { + const vk::DescriptorPoolCreateInfo poolCreateInfo = { .flags = vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind, - .maxSets = 4, + .maxSets = 1, .poolSizeCount = Cast(poolSizes.size()), .pPoolSizes = poolSizes.data(), }; AbortIfFailed(device->m_Device.createDescriptorPool(&poolCreateInfo, nullptr, &m_DescriptorPool)); + eastl::vector immutableSamplers(texturesCount, m_Sampler); eastl::array descriptorLayoutBindings = { vk::DescriptorSetLayoutBinding{ - .binding = 0, + .binding = BUFFER_BINDING_INDEX, .descriptorType = vk::DescriptorType::eStorageBuffer, - .descriptorCount = Cast(m_Buffers.size()), + .descriptorCount = Cast(buffersCount), .stageFlags = vk::ShaderStageFlagBits::eAll, }, vk::DescriptorSetLayoutBinding{ - .binding = 1, - .descriptorType = vk::DescriptorType::eSampledImage, - .descriptorCount = Cast(m_Textures.size()), + .binding = TEXTURE_BINDING_INDEX, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = Cast(texturesCount), .stageFlags = vk::ShaderStageFlagBits::eAll, + .pImmutableSamplers = immutableSamplers.data(), }, }; const vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { @@ -71,17 +174,7 @@ RenderResourceManager::RenderResourceManager(const Device *device) RenderResourceManager::~RenderResourceManager() { - for (auto &buffer : m_Buffers) - { - buffer.Destroy(m_Device); - } - for (auto &image : m_Textures) - { - image.Destroy(m_Device); - } - - m_Device->m_Device.destroy(m_TextureUpdate, nullptr); - m_Device->m_Device.destroy(m_BufferUpdate, nullptr); + m_Device->m_Device.destroy(m_Sampler, nullptr); m_Device->m_Device.destroy(m_DescriptorPool, nullptr); m_Device->m_Device.destroy(m_SetLayout, nullptr); } \ No newline at end of file diff --git a/samples/03_model_render/render_resource_manager.h b/samples/03_model_render/render_resource_manager.h index fe74305..1013789 100644 --- a/samples/03_model_render/render_resource_manager.h +++ b/samples/03_model_render/render_resource_manager.h @@ -7,16 +7,18 @@ #include "global.h" -#include -#include +#include +#include struct Device; struct Texture; -struct UniformBuffer; +struct UniformStorageBuffer; + +struct RenderResourceManager; struct Handle { - u16 m_Index; + u32 m_Index; }; struct BufferHandle : Handle @@ -27,29 +29,81 @@ struct TextureHandle : Handle { }; +struct FreeList +{ + eastl::stack> m_List; + u32 m_MaxVisited = 0; + u32 m_MaxCapacity = 16; + + void + Init(u32 maxCapacity) + { + m_MaxCapacity = maxCapacity; + } + + [[nodiscard]] u32 + Alloc() + { + if (!m_List.empty()) + { + const u32 value = m_List.top(); + m_List.pop(); + return value; + } + if (m_MaxVisited < m_MaxCapacity) + { + return m_MaxVisited++; + } + ERROR("Out of Handles.") THEN_ABORT(-1); + } + + void + Free(u32 index) + { + WARN_IF(index >= m_MaxCapacity, "Trying to free an out-of-bounds index."); + + if (index < m_MaxCapacity) + m_List.push(index); + } +}; + struct RenderResourceManager { + private: + union WriteInfo { + vk::DescriptorBufferInfo uBufferInfo; + vk::DescriptorImageInfo uImageInfo; + vk::BufferView uBufferView; + + explicit WriteInfo(vk::DescriptorBufferInfo info); + explicit WriteInfo(vk::DescriptorImageInfo info); + explicit WriteInfo(vk::BufferView info); + }; + + eastl::deque m_WriteInfos; + eastl::vector m_Writes; + + vk::Sampler m_Sampler; + + FreeList m_BufferFreeList; + FreeList m_TextureFreeList; + + public: const Device *m_Device; + constexpr static u32 BUFFER_BINDING_INDEX = 0; + constexpr static u32 TEXTURE_BINDING_INDEX = 1; + vk::DescriptorPool m_DescriptorPool; vk::DescriptorSetLayout m_SetLayout; vk::DescriptorSet m_DescriptorSet; - vk::DescriptorUpdateTemplate m_BufferUpdate; - vk::DescriptorUpdateTemplate m_TextureUpdate; + BufferHandle Commit(const UniformStorageBuffer *storageBuffer); + TextureHandle Commit(const Texture *texture); - eastl::vector m_Buffers; - eastl::vector m_Textures; - // TODO: eastl::vector m_Textures; - - // UniformBuffer *Allocate(); - // UniformBuffer *Fetch(UniformHandle handle); - // void Commit(UniformHandle handle); - // Texture *Allocate(); - // Texture *Fetch(TextureHandle handle); - // void Commit(TextureHandle handle); + void Update(); // Ctor/Dtor - explicit RenderResourceManager(const Device *device); + explicit RenderResourceManager(const Device *device, u16 maxSize); ~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 5573ea1..f1670eb 100644 --- a/samples/03_model_render/shader/model.frag.glsl +++ b/samples/03_model_render/shader/model.frag.glsl @@ -1,11 +1,16 @@ #version 450 #pragma shader_stage(fragment) +#extension GL_EXT_nonuniform_qualifier : enable layout (location = 0) in vec2 inUV; layout (location = 0) out vec4 outColor; -layout(set = 1, binding = 1) uniform sampler2D tex; +layout(set = 0, binding = 1) uniform sampler2D textures[]; + +layout(push_constant) uniform Block { + uint handle; +}; void main() { - outColor = vec4(texture(tex, inUV).rgb, 1.0f); + outColor = vec4(texture(textures[handle], 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 96720f9..4463ab3 100644 --- a/samples/03_model_render/shader/model.vert.glsl +++ b/samples/03_model_render/shader/model.vert.glsl @@ -1,5 +1,6 @@ #version 450 #pragma shader_stage(vertex) +#extension GL_EXT_nonuniform_qualifier : enable layout(location = 0) in vec4 position; layout(location = 1) in vec2 uv0; @@ -7,7 +8,7 @@ layout(location = 1) in vec2 uv0; layout(location = 0) out vec2 outUV; //layout(std140, set=0, binding=0) readonly buffer buffers; -layout(set = 0, binding = 1) uniform texture2D textures[]; +//layout(set = 0, binding = 1) uniform texture2D textures[]; layout(set = 1, binding = 0) uniform Camera { mat4 model;