diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp index 31a5c0867..a92be58f6 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp @@ -8,7 +8,6 @@ #define NAZARA_OPENGLRENDERER_OPENGLCOMMANDBUFFER_HPP #include -#include #include #include #include @@ -17,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -46,8 +46,11 @@ namespace Nz inline void BindRenderPipeline(const OpenGLRenderPipeline* pipeline); inline void BindRenderShaderBinding(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 set, const OpenGLShaderBinding* binding); inline void BindVertexBuffer(UInt32 binding, GLuint vertexBuffer, UInt64 offset = 0); + inline void BlitTexture(const OpenGLTexture& source, const Boxui& sourceBox, const OpenGLTexture& target, const Boxui& targetBox, SamplerFilter filter = SamplerFilter::Nearest); + inline void BuildMipmaps(OpenGLTexture& texture, UInt8 baseLevel, UInt8 levelCount); + inline void CopyBuffer(GLuint source, GLuint target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0); inline void CopyBuffer(const UploadPool::Allocation& allocation, GLuint target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0); inline void CopyTexture(const OpenGLTexture& source, const Boxui& sourceBox, const OpenGLTexture& target, const Vector3ui& targetPoint); @@ -83,6 +86,7 @@ namespace Nz #define NAZARA_OPENGL_FOREACH_COMMANDS(cb, lastCb) \ cb(BeginDebugRegionCommand) \ cb(BlitTextureCommand) \ + cb(BuildTextureMipmapsCommand) \ cb(CopyBufferCommand) \ cb(CopyBufferFromMemoryCommand) \ cb(CopyTextureCommand) \ @@ -112,6 +116,7 @@ namespace Nz inline void Execute(const GL::Context* context, const BeginDebugRegionCommand& command); inline void Execute(const GL::Context* context, const BlitTextureCommand& command); + inline void Execute(const GL::Context* context, const BuildTextureMipmapsCommand& command); inline void Execute(const GL::Context* context, const CopyBufferCommand& command); inline void Execute(const GL::Context* context, const CopyBufferFromMemoryCommand& command); inline void Execute(const GL::Context* context, const CopyTextureCommand& command); @@ -139,6 +144,13 @@ namespace Nz SamplerFilter filter; }; + struct BuildTextureMipmapsCommand + { + OpenGLTexture* texture; + UInt8 baseLevel; + UInt8 levelCount; + }; + struct ComputeStates { const OpenGLComputePipeline* pipeline = nullptr; diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl index 8bf7f602b..8459506e5 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl @@ -88,6 +88,17 @@ namespace Nz m_commands.emplace_back(std::move(blitTexture)); } + inline void OpenGLCommandBuffer::BuildMipmaps(OpenGLTexture& texture, UInt8 baseLevel, UInt8 levelCount) + { + BuildTextureMipmapsCommand buildMipmaps = { + &texture, + baseLevel, + levelCount + }; + + m_commands.emplace_back(std::move(buildMipmaps)); + } + inline void OpenGLCommandBuffer::CopyBuffer(GLuint source, GLuint target, UInt64 size, UInt64 sourceOffset, UInt64 targetOffset) { CopyBufferCommand copyBuffer = { diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp b/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp index 8bd9f3697..f1a30b08b 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp @@ -38,6 +38,8 @@ namespace Nz void BlitTexture(const Texture& fromTexture, const Boxui& fromBox, TextureLayout fromLayout, const Texture& toTexture, const Boxui& toBox, TextureLayout toLayout, SamplerFilter filter) override; + void BuildMipmaps(Texture& texture, UInt8 baseLevel, UInt8 maxLevel) override; + void CopyBuffer(const RenderBufferView& source, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0) override; void CopyBuffer(const UploadPool::Allocation& allocation, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0) override; void CopyTexture(const Texture& fromTexture, const Boxui& fromBox, TextureLayout fromLayout, const Texture& toTexture, const Vector3ui& toPos, TextureLayout toLayout) override; diff --git a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp index ee396025f..cd9a5a784 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp @@ -47,6 +47,7 @@ namespace Nz std::shared_ptr InstantiateShaderModule(nzsl::ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const nzsl::ShaderWriter::States& states) override; std::shared_ptr InstantiateSwapchain(WindowHandle windowHandle, const Vector2ui& windowSize, const SwapchainParameters& parameters) override; std::shared_ptr InstantiateTexture(const TextureInfo& params) override; + std::shared_ptr InstantiateTexture(const TextureInfo& params, const void* initialData, bool buildMipmaps, unsigned int srcWidth = 0, unsigned int srcHeight = 0) override; std::shared_ptr InstantiateTextureSampler(const TextureSamplerInfo& params) override; bool IsTextureFormatSupported(PixelFormat format, TextureUsage usage) const override; diff --git a/include/Nazara/OpenGLRenderer/OpenGLTexture.hpp b/include/Nazara/OpenGLRenderer/OpenGLTexture.hpp index 579df639b..6b5688957 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLTexture.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLTexture.hpp @@ -20,6 +20,7 @@ namespace Nz { public: OpenGLTexture(OpenGLDevice& device, const TextureInfo& textureInfo); + OpenGLTexture(OpenGLDevice& device, const TextureInfo& textureInfo, const void* initialData, bool buildMipmaps, unsigned int srcWidth = 0, unsigned int srcHeight = 0); OpenGLTexture(std::shared_ptr parentTexture, const TextureViewInfo& viewInfo); OpenGLTexture(const OpenGLTexture&) = delete; OpenGLTexture(OpenGLTexture&&) = delete; @@ -28,6 +29,8 @@ namespace Nz bool Copy(const Texture& source, const Boxui& srcBox, const Vector3ui& dstPos) override; std::shared_ptr CreateView(const TextureViewInfo& viewInfo) override; + inline void GenerateMipmaps(UInt8 baseLevel, UInt8 levelCount); + inline PixelFormat GetFormat() const override; inline UInt8 GetLevelCount() const override; inline OpenGLTexture* GetParentTexture() const override; diff --git a/include/Nazara/OpenGLRenderer/OpenGLTexture.inl b/include/Nazara/OpenGLRenderer/OpenGLTexture.inl index 0269ac17c..4ad253a36 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLTexture.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLTexture.inl @@ -2,11 +2,42 @@ // This file is part of the "Nazara Engine - OpenGL renderer" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include namespace Nz { + inline void OpenGLTexture::GenerateMipmaps(UInt8 baseLevel, UInt8 levelCount) + { + NazaraAssert(baseLevel + levelCount < m_textureInfo.levelCount, "out of bounds"); + + GL::Texture* targetTexture; + if (RequiresTextureViewEmulation()) + { + baseLevel += m_viewInfo->baseMipLevel; + levelCount += m_viewInfo->baseMipLevel; + targetTexture = &m_parentTexture->m_texture; + } + else + targetTexture = &m_texture; + + if (baseLevel != 0) + targetTexture->SetParameteri(GL_TEXTURE_BASE_LEVEL, baseLevel); + + if (levelCount != m_textureInfo.levelCount) + targetTexture->SetParameteri(GL_TEXTURE_MAX_LEVEL, levelCount); + + targetTexture->GenerateMipmap(); + + // Reset level config + if (baseLevel != 0) + targetTexture->SetParameteri(GL_TEXTURE_BASE_LEVEL, 0); + + if (levelCount != m_textureInfo.levelCount) + targetTexture->SetParameteri(GL_TEXTURE_MAX_LEVEL, m_textureInfo.levelCount); + } + inline PixelFormat OpenGLTexture::GetFormat() const { return m_textureInfo.pixelFormat; diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Texture.hpp b/include/Nazara/OpenGLRenderer/Wrapper/Texture.hpp index cd2e26d1f..5f065b80d 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Texture.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/Texture.hpp @@ -24,6 +24,8 @@ namespace Nz::GL Texture(Texture&&) noexcept = default; ~Texture() = default; + inline void GenerateMipmap(); + inline TextureTarget GetTarget() const; inline void SetParameterf(GLenum pname, GLfloat param); diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Texture.inl b/include/Nazara/OpenGLRenderer/Wrapper/Texture.inl index 54f687a61..4b9478dfe 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Texture.inl +++ b/include/Nazara/OpenGLRenderer/Wrapper/Texture.inl @@ -7,6 +7,13 @@ namespace Nz::GL { + inline void Texture::GenerateMipmap() + { + const Context& context = EnsureDeviceContext(); + context.BindTexture(m_target, m_objectId); + context.glGenerateMipmap(ToOpenGL(m_target)); + } + inline TextureTarget Texture::GetTarget() const { return m_target; diff --git a/include/Nazara/Renderer/CommandBufferBuilder.hpp b/include/Nazara/Renderer/CommandBufferBuilder.hpp index 9d027ff74..6c879393a 100644 --- a/include/Nazara/Renderer/CommandBufferBuilder.hpp +++ b/include/Nazara/Renderer/CommandBufferBuilder.hpp @@ -54,6 +54,8 @@ namespace Nz virtual void BlitTexture(const Texture& fromTexture, const Boxui& fromBox, TextureLayout fromLayout, const Texture& toTexture, const Boxui& toBox, TextureLayout toLayout, SamplerFilter filter) = 0; + virtual void BuildMipmaps(Texture& texture, UInt8 baseLevel, UInt8 maxLevel) = 0; + inline void CopyBuffer(const RenderBufferView& source, const RenderBufferView& target); virtual void CopyBuffer(const RenderBufferView& source, const RenderBufferView& target, UInt64 size, UInt64 fromOffset = 0, UInt64 toOffset = 0) = 0; inline void CopyBuffer(const UploadPool::Allocation& allocation, const RenderBufferView& target); diff --git a/include/Nazara/Renderer/RenderDevice.hpp b/include/Nazara/Renderer/RenderDevice.hpp index 09d635515..7668457a4 100644 --- a/include/Nazara/Renderer/RenderDevice.hpp +++ b/include/Nazara/Renderer/RenderDevice.hpp @@ -53,6 +53,7 @@ namespace Nz std::shared_ptr InstantiateShaderModule(nzsl::ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const std::filesystem::path& sourcePath, const nzsl::ShaderWriter::States& states); virtual std::shared_ptr InstantiateSwapchain(WindowHandle windowHandle, const Vector2ui& windowSize, const SwapchainParameters& parameters) = 0; virtual std::shared_ptr InstantiateTexture(const TextureInfo& params) = 0; + virtual std::shared_ptr InstantiateTexture(const TextureInfo& params, const void* initialData, bool buildMipmaps, unsigned int srcWidth = 0, unsigned int srcHeight = 0) = 0; virtual std::shared_ptr InstantiateTextureSampler(const TextureSamplerInfo& params) = 0; virtual bool IsTextureFormatSupported(PixelFormat format, TextureUsage usage) const = 0; diff --git a/include/Nazara/Renderer/Texture.hpp b/include/Nazara/Renderer/Texture.hpp index b5e388eea..7c742b0f7 100644 --- a/include/Nazara/Renderer/Texture.hpp +++ b/include/Nazara/Renderer/Texture.hpp @@ -25,8 +25,8 @@ namespace Nz { PixelFormat pixelFormat; ImageType type; - TextureUsageFlags usageFlags = TextureUsage::ShaderSampling | TextureUsage::TransferDestination; - UInt8 levelCount = 1; + TextureUsageFlags usageFlags = TextureUsage::ShaderSampling | TextureUsage::TransferDestination | TextureUsage::TransferSource; + UInt8 levelCount = 0xFF; unsigned int layerCount = 1; unsigned int depth = 1; unsigned int height; @@ -46,7 +46,8 @@ namespace Nz struct NAZARA_RENDERER_API TextureParams : ImageParams { std::shared_ptr renderDevice; - TextureUsageFlags usageFlags = TextureUsage::ShaderSampling | TextureUsage::TransferDestination; + TextureUsageFlags usageFlags = TextureUsage::ShaderSampling | TextureUsage::TransferDestination | TextureUsage::TransferSource; + bool buildMipmaps = true; bool IsValid() const; void Merge(const TextureParams& params); diff --git a/include/Nazara/Utility/Enums.hpp b/include/Nazara/Utility/Enums.hpp index bd44dd1f0..43deebe57 100644 --- a/include/Nazara/Utility/Enums.hpp +++ b/include/Nazara/Utility/Enums.hpp @@ -62,8 +62,9 @@ namespace Nz Vertex, Storage, Uniform, + Upload, - Max = Uniform + Max = Upload }; enum class BufferUsage diff --git a/include/Nazara/Utility/Image.hpp b/include/Nazara/Utility/Image.hpp index 0a3615b2d..0efe9206e 100644 --- a/include/Nazara/Utility/Image.hpp +++ b/include/Nazara/Utility/Image.hpp @@ -110,9 +110,11 @@ namespace Nz Image& operator=(const Image& image); inline Image& operator=(Image&& image) noexcept; + static inline void ArrayToRegion(ImageType type, unsigned int baseLayer, unsigned int layerCount, Boxui& region); static void Copy(UInt8* destination, const UInt8* source, PixelFormat format, unsigned int width, unsigned int height, unsigned int depth = 1, unsigned int dstWidth = 0, unsigned int dstHeight = 0, unsigned int srcWidth = 0, unsigned int srcHeight = 0); static UInt8 GetMaxLevel(unsigned int width, unsigned int height, unsigned int depth = 1); static UInt8 GetMaxLevel(ImageType type, unsigned int width, unsigned int height, unsigned int depth = 1); + static inline Boxui RegionToArray(ImageType type, Boxui region, unsigned int& baseLayer, unsigned int& layerCount); // Load static std::shared_ptr LoadFromFile(const std::filesystem::path& filePath, const ImageParams& params = ImageParams()); diff --git a/include/Nazara/Utility/Image.inl b/include/Nazara/Utility/Image.inl index 34a312fd2..9538deb15 100644 --- a/include/Nazara/Utility/Image.inl +++ b/include/Nazara/Utility/Image.inl @@ -18,6 +18,70 @@ namespace Nz return *this; } + + inline void Image::ArrayToRegion(ImageType type, unsigned int baseLayer, unsigned int layerCount, Boxui& region) + { + switch (type) + { + case ImageType::E1D_Array: + region.y = baseLayer; + region.height = layerCount; + break; + + case ImageType::Cubemap: + case ImageType::E2D_Array: + region.z = baseLayer; + region.depth = layerCount; + break; + + case ImageType::E1D: + NazaraAssert(baseLayer == 0, "out of bounds"); + NazaraAssert(layerCount <= 1, "out of bounds"); + case ImageType::E2D: + NazaraAssert(baseLayer == 0, "out of bounds"); + NazaraAssert(layerCount <= 1, "out of bounds"); + case ImageType::E3D: + region.z = 0; + region.depth = 1; + break; + } + } + + inline Boxui Image::RegionToArray(ImageType type, Boxui region, unsigned int& baseLayer, unsigned int& layerCount) + { + switch (type) + { + case ImageType::E1D_Array: + baseLayer = region.y; + layerCount = region.height; + + region.y = 0; + region.height = 1; + break; + + case ImageType::Cubemap: + case ImageType::E2D_Array: + baseLayer = region.z; + layerCount = region.depth; + + region.z = 0; + region.depth = 1; + break; + + case ImageType::E1D: + NazaraAssert(region.y == 0, "out of bounds"); + NazaraAssert(region.height <= 1, "out of bounds"); + case ImageType::E2D: + NazaraAssert(region.z == 0, "out of bounds"); + NazaraAssert(region.depth <= 1, "out of bounds"); + case ImageType::E3D: + baseLayer = 0; + layerCount = 1; + break; + } + + return region; + } } #include diff --git a/include/Nazara/VulkanRenderer/Utils.inl b/include/Nazara/VulkanRenderer/Utils.inl index abf027ff3..db79ccaa2 100644 --- a/include/Nazara/VulkanRenderer/Utils.inl +++ b/include/Nazara/VulkanRenderer/Utils.inl @@ -100,10 +100,11 @@ namespace Nz { switch (bufferType) { - case BufferType::Index: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + case BufferType::Index: return VK_BUFFER_USAGE_INDEX_BUFFER_BIT; case BufferType::Storage: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - case BufferType::Vertex: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + case BufferType::Vertex: return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; case BufferType::Uniform: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + case BufferType::Upload: return VK_BUFFER_USAGE_TRANSFER_SRC_BIT; } NazaraError("Unhandled BufferType 0x" + NumberToString(UnderlyingCast(bufferType), 16)); diff --git a/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp b/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp index a3d9a7fb5..271bae143 100644 --- a/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp +++ b/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp @@ -38,6 +38,8 @@ namespace Nz void BlitTexture(const Texture& fromTexture, const Boxui& fromBox, TextureLayout fromLayout, const Texture& toTexture, const Boxui& toBox, TextureLayout toLayout, SamplerFilter filter) override; + void BuildMipmaps(Texture& texture, UInt8 baseLevel, UInt8 maxLevel) override; + void CopyBuffer(const RenderBufferView& source, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0) override; void CopyBuffer(const UploadPool::Allocation& allocation, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0) override; void CopyTexture(const Texture& fromTexture, const Boxui& fromBox, TextureLayout fromLayout, const Texture& toTexture, const Vector3ui& toPos, TextureLayout toLayout) override; diff --git a/include/Nazara/VulkanRenderer/VulkanDevice.hpp b/include/Nazara/VulkanRenderer/VulkanDevice.hpp index 0632c6c23..23de7ab14 100644 --- a/include/Nazara/VulkanRenderer/VulkanDevice.hpp +++ b/include/Nazara/VulkanRenderer/VulkanDevice.hpp @@ -37,6 +37,7 @@ namespace Nz std::shared_ptr InstantiateShaderModule(nzsl::ShaderStageTypeFlags stages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const nzsl::ShaderWriter::States& states) override; std::shared_ptr InstantiateSwapchain(WindowHandle windowHandle, const Vector2ui& windowSize, const SwapchainParameters& parameters) override; std::shared_ptr InstantiateTexture(const TextureInfo& params) override; + std::shared_ptr InstantiateTexture(const TextureInfo& params, const void* initialData, bool buildMipmaps, unsigned int srcWidth = 0, unsigned int srcHeight = 0) override; std::shared_ptr InstantiateTextureSampler(const TextureSamplerInfo& params) override; bool IsTextureFormatSupported(PixelFormat format, TextureUsage usage) const override; diff --git a/include/Nazara/VulkanRenderer/VulkanTexture.hpp b/include/Nazara/VulkanRenderer/VulkanTexture.hpp index 0d5626c2a..5df464c33 100644 --- a/include/Nazara/VulkanRenderer/VulkanTexture.hpp +++ b/include/Nazara/VulkanRenderer/VulkanTexture.hpp @@ -16,15 +16,29 @@ namespace Nz { + class VulkanBuffer; + class VulkanDevice; + + namespace Vk + { + class CommandBuffer; + } + class NAZARA_VULKANRENDERER_API VulkanTexture final : public Texture { public: - VulkanTexture(Vk::Device& device, const TextureInfo& textureInfo); + VulkanTexture(VulkanDevice& device, const TextureInfo& textureInfo); + VulkanTexture(VulkanDevice& device, const TextureInfo& textureInfo, const void* initialData, bool buildMipmaps, unsigned int srcWidth = 0, unsigned int srcHeight = 0); VulkanTexture(std::shared_ptr parentTexture, const TextureViewInfo& viewInfo); VulkanTexture(const VulkanTexture&) = delete; VulkanTexture(VulkanTexture&&) = delete; ~VulkanTexture(); + inline VkImageSubresourceLayers BuildSubresourceLayers(UInt32 level) const; + inline VkImageSubresourceLayers BuildSubresourceLayers(UInt32 level, UInt32 baseLayer, UInt32 layerCount) const; + inline VkImageSubresourceRange BuildSubresourceRange(UInt32 baseLevel, UInt32 levelCount) const; + inline VkImageSubresourceRange BuildSubresourceRange(UInt32 baseLevel, UInt32 levelCount, UInt32 baseLayer, UInt32 layerCount) const; + bool Copy(const Texture& source, const Boxui& srcBox, const Vector3ui& dstPos) override; std::shared_ptr CreateView(const TextureViewInfo& viewInfo) override; @@ -40,6 +54,7 @@ namespace Nz using Texture::Update; bool Update(const void* ptr, const Boxui& box, unsigned int srcWidth, unsigned int srcHeight, UInt8 level) override; + bool Update(Vk::CommandBuffer& commandBuffer, std::unique_ptr& uploadBuffer, const void* ptr, const Boxui& box, unsigned int srcWidth, unsigned int srcHeight, UInt8 level); void UpdateDebugName(std::string_view name) override; @@ -51,12 +66,13 @@ namespace Nz std::optional m_viewInfo; std::shared_ptr m_parentTexture; + VulkanDevice& m_device; VkImage m_image; - VkImageSubresourceRange m_imageRange; + VkImageSubresourceRange m_subresourceRange; VmaAllocation m_allocation; - Vk::Device& m_device; Vk::ImageView m_imageView; TextureInfo m_textureInfo; + TextureInfo m_textureViewInfo; }; } diff --git a/include/Nazara/VulkanRenderer/VulkanTexture.inl b/include/Nazara/VulkanRenderer/VulkanTexture.inl index 8c8800f8b..cb623c9bd 100644 --- a/include/Nazara/VulkanRenderer/VulkanTexture.inl +++ b/include/Nazara/VulkanRenderer/VulkanTexture.inl @@ -2,13 +2,53 @@ // This file is part of the "Nazara Engine - Vulkan renderer" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include namespace Nz { + inline VkImageSubresourceLayers VulkanTexture::BuildSubresourceLayers(UInt32 level) const + { + return BuildSubresourceLayers(level, 0, m_textureViewInfo.layerCount); + } + + inline VkImageSubresourceLayers VulkanTexture::BuildSubresourceLayers(UInt32 level, UInt32 baseLayer, UInt32 layerCount) const + { + NazaraAssert(m_subresourceRange.baseMipLevel + level < m_textureInfo.levelCount, "mipmap level out of bounds"); + NazaraAssert(m_subresourceRange.baseArrayLayer + baseLayer + layerCount <= m_textureInfo.layerCount, "mipmap level out of bounds"); + + VkImageSubresourceLayers subresourceLayers; + subresourceLayers.aspectMask = m_subresourceRange.aspectMask; + subresourceLayers.mipLevel = m_subresourceRange.baseMipLevel + level; + subresourceLayers.baseArrayLayer = baseLayer; + subresourceLayers.layerCount = layerCount; + + return subresourceLayers; + } + + inline VkImageSubresourceRange VulkanTexture::BuildSubresourceRange(UInt32 baseLevel, UInt32 levelCount) const + { + return BuildSubresourceRange(baseLevel, levelCount, 0, m_textureInfo.layerCount); + } + + inline VkImageSubresourceRange VulkanTexture::BuildSubresourceRange(UInt32 baseLevel, UInt32 levelCount, UInt32 baseLayer, UInt32 layerCount) const + { + VkImageSubresourceLayers subresourceLayers = BuildSubresourceLayers(baseLevel, baseLayer, layerCount); + + NazaraAssert(subresourceLayers.mipLevel + levelCount <= m_textureInfo.levelCount, "mipmap level out of bounds"); + + return { + subresourceLayers.aspectMask, + subresourceLayers.mipLevel, + levelCount, + subresourceLayers.baseArrayLayer, + subresourceLayers.layerCount + }; + } + inline PixelFormat VulkanTexture::GetFormat() const { - return m_textureInfo.pixelFormat; + return m_textureViewInfo.pixelFormat; } inline VkImage VulkanTexture::GetImage() const @@ -23,7 +63,7 @@ namespace Nz inline UInt8 VulkanTexture::GetLevelCount() const { - return m_textureInfo.levelCount; + return m_textureViewInfo.levelCount; } inline VulkanTexture* VulkanTexture::GetParentTexture() const @@ -33,22 +73,22 @@ namespace Nz inline Vector3ui VulkanTexture::GetSize(UInt8 level) const { - return Vector3ui(GetLevelSize(m_textureInfo.width, level), GetLevelSize(m_textureInfo.height, level), GetLevelSize(m_textureInfo.depth, level)); + return Vector3ui(GetLevelSize(m_textureViewInfo.width, level), GetLevelSize(m_textureViewInfo.height, level), GetLevelSize(m_textureViewInfo.depth, level)); } inline const VkImageSubresourceRange& VulkanTexture::GetSubresourceRange() const { - return m_imageRange; + return m_subresourceRange; } inline const TextureInfo& VulkanTexture::GetTextureInfo() const { - return m_textureInfo; + return m_textureViewInfo; } inline ImageType VulkanTexture::GetType() const { - return m_textureInfo.type; + return m_textureViewInfo.type; } } diff --git a/src/Nazara/Graphics/BakedFrameGraph.cpp b/src/Nazara/Graphics/BakedFrameGraph.cpp index f49b5f970..1a9195059 100644 --- a/src/Nazara/Graphics/BakedFrameGraph.cpp +++ b/src/Nazara/Graphics/BakedFrameGraph.cpp @@ -168,6 +168,7 @@ namespace Nz textureCreationParams.type = textureData.type; textureCreationParams.usageFlags = textureData.usage; textureCreationParams.pixelFormat = textureData.format; + textureCreationParams.levelCount = 1; if (textureCreationParams.type == ImageType::Cubemap) textureCreationParams.layerCount = 6; diff --git a/src/Nazara/Graphics/Graphics.cpp b/src/Nazara/Graphics/Graphics.cpp index 62cb50094..344b2685b 100644 --- a/src/Nazara/Graphics/Graphics.cpp +++ b/src/Nazara/Graphics/Graphics.cpp @@ -361,8 +361,7 @@ namespace Nz texInfo.layerCount = (texInfo.type == ImageType::Cubemap) ? 6 : 1; - m_defaultTextures.depthTextures[i] = m_renderDevice->InstantiateTexture(texInfo); - m_defaultTextures.depthTextures[i]->Update(whitePixels.data()); + m_defaultTextures.depthTextures[i] = m_renderDevice->InstantiateTexture(texInfo, whitePixels.data(), false); } } @@ -379,8 +378,7 @@ namespace Nz texInfo.type = static_cast(i); texInfo.layerCount = (texInfo.type == ImageType::Cubemap) ? 6 : 1; - m_defaultTextures.whiteTextures[i] = m_renderDevice->InstantiateTexture(texInfo); - m_defaultTextures.whiteTextures[i]->Update(whitePixels.data()); + m_defaultTextures.whiteTextures[i] = m_renderDevice->InstantiateTexture(texInfo, whitePixels.data(), false); } } } diff --git a/src/Nazara/Graphics/RenderBufferPool.cpp b/src/Nazara/Graphics/RenderBufferPool.cpp index 975b200b6..5a63705e9 100644 --- a/src/Nazara/Graphics/RenderBufferPool.cpp +++ b/src/Nazara/Graphics/RenderBufferPool.cpp @@ -20,6 +20,7 @@ namespace Nz { case BufferType::Index: case BufferType::Vertex: + case BufferType::Upload: break; // TODO case BufferType::Storage: diff --git a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp index 7a49f59ff..fb8ab9d29 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -145,6 +146,11 @@ namespace Nz context->BlitTexture(*command.source, *command.target, command.sourceBox, command.targetBox, command.filter); } + inline void OpenGLCommandBuffer::Execute(const GL::Context* /*context*/, const BuildTextureMipmapsCommand& command) + { + command.texture->GenerateMipmaps(command.baseLevel, command.levelCount); + } + inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const CopyBufferCommand& command) { context->BindBuffer(GL::BufferTarget::CopyRead, command.source); @@ -197,7 +203,7 @@ namespace Nz context->glDrawElementsInstanced(ToOpenGL(command.states.pipeline->GetPipelineInfo().primitiveMode), command.indexCount, ToOpenGL(command.states.indexBufferType), origin, command.instanceCount); } - inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const EndDebugRegionCommand& command) + inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const EndDebugRegionCommand& /*command*/) { if (context->glPopDebugGroup) context->glPopDebugGroup(); diff --git a/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp b/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp index c3318fd50..f4dc05ced 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp @@ -93,6 +93,13 @@ namespace Nz m_commandBuffer.BlitTexture(sourceTexture, fromBox, targetTexture, toBox, filter); } + void OpenGLCommandBufferBuilder::BuildMipmaps(Texture& texture, UInt8 baseLevel, UInt8 maxLevel) + { + OpenGLTexture& glTexture = static_cast(texture); + + glTexture.GenerateMipmaps(baseLevel, maxLevel); + } + void OpenGLCommandBufferBuilder::CopyBuffer(const RenderBufferView& source, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset, UInt64 targetOffset) { OpenGLBuffer& sourceBuffer = *static_cast(source.GetBuffer()); diff --git a/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp b/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp index e398060a4..5a8c9d896 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp @@ -221,6 +221,11 @@ namespace Nz return std::make_shared(*this, params); } + std::shared_ptr OpenGLDevice::InstantiateTexture(const TextureInfo& params, const void* initialData, bool buildMipmaps, unsigned int srcWidth, unsigned int srcHeight) + { + return std::make_shared(*this, params, initialData, buildMipmaps, srcWidth, srcHeight); + } + std::shared_ptr OpenGLDevice::InstantiateTextureSampler(const TextureSamplerInfo& params) { return std::make_shared(*this, params); diff --git a/src/Nazara/OpenGLRenderer/OpenGLTexture.cpp b/src/Nazara/OpenGLRenderer/OpenGLTexture.cpp index 4e6b4114a..b535d41d7 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLTexture.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLTexture.cpp @@ -13,6 +13,8 @@ namespace Nz OpenGLTexture::OpenGLTexture(OpenGLDevice& device, const TextureInfo& textureInfo) : m_textureInfo(textureInfo) { + m_textureInfo.levelCount = std::min(m_textureInfo.levelCount, Image::GetMaxLevel(m_textureInfo.type, m_textureInfo.width, m_textureInfo.height, m_textureInfo.depth)); + if (!m_texture.Create(device)) throw std::runtime_error("failed to create texture object"); @@ -71,6 +73,16 @@ namespace Nz #endif } + OpenGLTexture::OpenGLTexture(OpenGLDevice& device, const TextureInfo& textureInfo, const void* initialData, bool buildMipmaps, unsigned int srcWidth, unsigned int srcHeight) : + OpenGLTexture(device, textureInfo) + { + NazaraAssert(initialData, "missing initial data"); + + Update(initialData, srcWidth, srcHeight, 0); + if (buildMipmaps && m_textureInfo.levelCount > 1) + m_texture.GenerateMipmap(); + } + OpenGLTexture::OpenGLTexture(std::shared_ptr parentTexture, const TextureViewInfo& viewInfo) : m_parentTexture(std::move(parentTexture)) { diff --git a/src/Nazara/Renderer/Texture.cpp b/src/Nazara/Renderer/Texture.cpp index 12ce02acf..ce8b0b4e0 100644 --- a/src/Nazara/Renderer/Texture.cpp +++ b/src/Nazara/Renderer/Texture.cpp @@ -46,8 +46,7 @@ namespace Nz { NazaraAssert(params.IsValid(), "Invalid TextureParams"); - Nz::TextureInfo texParams; - texParams.height = image.GetHeight(); + TextureInfo texParams; texParams.pixelFormat = image.GetFormat(); texParams.type = image.GetType(); texParams.width = image.GetWidth(); @@ -56,32 +55,38 @@ namespace Nz switch (image.GetType()) { case ImageType::E1D: + texParams.height = 1; + break; + case ImageType::E2D: + texParams.height = image.GetHeight(); + break; + case ImageType::E3D: + texParams.height = image.GetHeight(); + texParams.depth = image.GetDepth(); break; case ImageType::E1D_Array: + texParams.height = 1; texParams.layerCount = image.GetHeight(); break; case ImageType::E2D_Array: + texParams.height = image.GetHeight(); texParams.layerCount = image.GetDepth(); break; case ImageType::Cubemap: + texParams.height = image.GetHeight(); texParams.layerCount = 6; break; } - std::shared_ptr texture = params.renderDevice->InstantiateTexture(texParams); - if (!texture->Update(image.GetConstPixels())) - { - NazaraError("failed to update texture"); - return {}; - } + std::shared_ptr texture = params.renderDevice->InstantiateTexture(texParams, image.GetConstPixels(), params.buildMipmaps); texture->SetFilePath(image.GetFilePath()); - if (std::string debugName = image.GetFilePath().generic_u8string(); !debugName.empty()) + if (std::string debugName = texture->GetFilePath().generic_u8string(); !debugName.empty()) texture->UpdateDebugName(debugName); return texture; diff --git a/src/Nazara/Utility/Image.cpp b/src/Nazara/Utility/Image.cpp index 0ca3c6dc6..a18144f25 100644 --- a/src/Nazara/Utility/Image.cpp +++ b/src/Nazara/Utility/Image.cpp @@ -1351,7 +1351,7 @@ namespace Nz UInt8 Image::GetMaxLevel(unsigned int width, unsigned int height, unsigned int depth) { // Le niveau maximal est le niveau requis pour la plus grande taille - return SafeCast(std::max(IntegralLog2(std::max({width, height, depth})), 1U)); + return SafeCast(std::max(IntegralLog2(std::max({ width, height, depth })), 1U)); } UInt8 Image::GetMaxLevel(ImageType type, unsigned int width, unsigned int height, unsigned int depth) diff --git a/src/Nazara/VulkanRenderer/VulkanBuffer.cpp b/src/Nazara/VulkanRenderer/VulkanBuffer.cpp index e80bd959f..20414d619 100644 --- a/src/Nazara/VulkanRenderer/VulkanBuffer.cpp +++ b/src/Nazara/VulkanRenderer/VulkanBuffer.cpp @@ -28,7 +28,9 @@ namespace Nz //TODO: Update for VMA 3.0 VmaAllocationCreateInfo allocInfo = {}; - if (usage & BufferUsage::DeviceLocal) + if (type == BufferType::Upload) + allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; + else if (usage & BufferUsage::DeviceLocal) { if (usage & BufferUsage::DirectMapping) allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; diff --git a/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp b/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp index db392208d..a3804cb35 100644 --- a/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp +++ b/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp @@ -141,15 +141,14 @@ namespace Nz const VulkanTexture& vkFromTexture = static_cast(fromTexture); const VulkanTexture& vkToTexture = static_cast(toTexture); - VkImageSubresourceLayers todo = { - VK_IMAGE_ASPECT_COLOR_BIT, - 1, - 0, - 1 - }; + unsigned int fromBaseLayer, fromLayerCount; + Image::RegionToArray(vkFromTexture.GetType(), fromBox, fromBaseLayer, fromLayerCount); + + unsigned int toBaseLayer, toLayerCount; + Image::RegionToArray(vkToTexture.GetType(), toBox, toBaseLayer, toLayerCount); VkImageBlit region = { - todo, + vkFromTexture.BuildSubresourceLayers(0, fromBaseLayer, fromLayerCount), { { SafeCast(fromBox.x), @@ -162,7 +161,7 @@ namespace Nz SafeCast(fromBox.z + fromBox.depth) } }, - todo, + vkToTexture.BuildSubresourceLayers(0, toBaseLayer, toLayerCount), { { SafeCast(toBox.x), @@ -180,6 +179,13 @@ namespace Nz m_commandBuffer.BlitImage(vkFromTexture.GetImage(), ToVulkan(fromLayout), vkToTexture.GetImage(), ToVulkan(toLayout), region, ToVulkan(filter)); } + void VulkanCommandBufferBuilder::BuildMipmaps(Texture& texture, UInt8 baseLevel, UInt8 maxLevel) + { + VulkanTexture& vkTexture = static_cast(texture); + + // TODO + } + void VulkanCommandBufferBuilder::CopyBuffer(const RenderBufferView& source, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset, UInt64 targetOffset) { VulkanBuffer& sourceBuffer = *static_cast(source.GetBuffer()); @@ -201,21 +207,20 @@ namespace Nz const VulkanTexture& vkFromTexture = static_cast(fromTexture); const VulkanTexture& vkToTexture = static_cast(toTexture); - VkImageSubresourceLayers todo = { - VK_IMAGE_ASPECT_COLOR_BIT, - 1, - 0, - 1 - }; + unsigned int fromBaseLayer, fromLayerCount; + Image::RegionToArray(vkFromTexture.GetType(), fromBox, fromBaseLayer, fromLayerCount); + + unsigned int toBaseLayer, toLayerCount; + Image::RegionToArray(vkToTexture.GetType(), Boxui(toPos.x, toPos.y, toPos.z, fromBox.width, fromBox.height, fromBox.depth), toBaseLayer, toLayerCount); VkImageCopy region = { - todo, + vkFromTexture.BuildSubresourceLayers(0, fromBaseLayer, fromLayerCount), { SafeCast(fromBox.x), SafeCast(fromBox.y), SafeCast(fromBox.z) }, - todo, + vkToTexture.BuildSubresourceLayers(0, toBaseLayer, toLayerCount), { SafeCast(toPos.x), SafeCast(toPos.y), diff --git a/src/Nazara/VulkanRenderer/VulkanDevice.cpp b/src/Nazara/VulkanRenderer/VulkanDevice.cpp index 12f98b6c5..cb833e856 100644 --- a/src/Nazara/VulkanRenderer/VulkanDevice.cpp +++ b/src/Nazara/VulkanRenderer/VulkanDevice.cpp @@ -96,6 +96,11 @@ namespace Nz return std::make_shared(*this, params); } + std::shared_ptr VulkanDevice::InstantiateTexture(const TextureInfo& params, const void* initialData, bool buildMipmaps, unsigned int srcWidth, unsigned int srcHeight) + { + return std::make_shared(*this, params, initialData, buildMipmaps, srcWidth, srcHeight); + } + std::shared_ptr VulkanDevice::InstantiateTextureSampler(const TextureSamplerInfo& params) { return std::make_shared(*this, params); diff --git a/src/Nazara/VulkanRenderer/VulkanTexture.cpp b/src/Nazara/VulkanRenderer/VulkanTexture.cpp index 775f30979..ee7c1aaa6 100644 --- a/src/Nazara/VulkanRenderer/VulkanTexture.cpp +++ b/src/Nazara/VulkanRenderer/VulkanTexture.cpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include #include @@ -13,15 +15,18 @@ namespace Nz { - VulkanTexture::VulkanTexture(Vk::Device& device, const TextureInfo& textureInfo) : + VulkanTexture::VulkanTexture(VulkanDevice& device, const TextureInfo& textureInfo) : + m_device(device), m_image(VK_NULL_HANDLE), m_allocation(nullptr), - m_device(device), - m_textureInfo(textureInfo) + m_textureInfo(textureInfo), + m_textureViewInfo(textureInfo) { + m_textureInfo.levelCount = std::min(m_textureInfo.levelCount, Image::GetMaxLevel(m_textureInfo.type, m_textureInfo.width, m_textureInfo.height, m_textureInfo.depth)); + VkImageViewCreateInfo createInfoView = {}; createInfoView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - InitViewForFormat(textureInfo.pixelFormat, createInfoView); + InitViewForFormat(m_textureInfo.pixelFormat, createInfoView); VkImageCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; @@ -29,65 +34,65 @@ namespace Nz createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; createInfo.samples = VK_SAMPLE_COUNT_1_BIT; createInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - createInfo.usage = ToVulkan(textureInfo.usageFlags); + createInfo.usage = ToVulkan(m_textureInfo.usageFlags); - switch (textureInfo.type) + switch (m_textureInfo.type) { case ImageType::E1D: - NazaraAssert(textureInfo.width > 0, "Width must be over zero"); - NazaraAssert(textureInfo.height == 1, "Height must be one"); - NazaraAssert(textureInfo.depth == 1, "Depth must be one"); - NazaraAssert(textureInfo.layerCount == 1, "Array count must be one"); + NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); + NazaraAssert(m_textureInfo.height == 1, "Height must be one"); + NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); + NazaraAssert(m_textureInfo.layerCount == 1, "Array count must be one"); createInfo.imageType = VK_IMAGE_TYPE_1D; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D; break; case ImageType::E1D_Array: - NazaraAssert(textureInfo.width > 0, "Width must be over zero"); - NazaraAssert(textureInfo.height == 1, "Height must be one"); - NazaraAssert(textureInfo.depth == 1, "Depth must be one"); - NazaraAssert(textureInfo.layerCount > 0, "Array count must be over zero"); + NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); + NazaraAssert(m_textureInfo.height == 1, "Height must be one"); + NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); + NazaraAssert(m_textureInfo.layerCount > 0, "Array count must be over zero"); createInfo.imageType = VK_IMAGE_TYPE_1D; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY; break; case ImageType::E2D: - NazaraAssert(textureInfo.width > 0, "Width must be over zero"); - NazaraAssert(textureInfo.height > 0, "Height must be over zero"); - NazaraAssert(textureInfo.depth == 1, "Depth must be one"); - NazaraAssert(textureInfo.layerCount == 1, "Array count must be one"); + NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); + NazaraAssert(m_textureInfo.height > 0, "Height must be over zero"); + NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); + NazaraAssert(m_textureInfo.layerCount == 1, "Array count must be one"); createInfo.imageType = VK_IMAGE_TYPE_2D; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D; break; case ImageType::E2D_Array: - NazaraAssert(textureInfo.width > 0, "Width must be over zero"); - NazaraAssert(textureInfo.height > 0, "Height must be over zero"); - NazaraAssert(textureInfo.depth == 1, "Depth must be one"); - NazaraAssert(textureInfo.layerCount > 0, "Array count must be over zero"); + NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); + NazaraAssert(m_textureInfo.height > 0, "Height must be over zero"); + NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); + NazaraAssert(m_textureInfo.layerCount > 0, "Array count must be over zero"); createInfo.imageType = VK_IMAGE_TYPE_2D; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; break; case ImageType::E3D: - NazaraAssert(textureInfo.width > 0, "Width must be over zero"); - NazaraAssert(textureInfo.height > 0, "Height must be over zero"); - NazaraAssert(textureInfo.depth > 0, "Depth must be over zero"); - NazaraAssert(textureInfo.layerCount == 1, "Array count must be one"); + NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); + NazaraAssert(m_textureInfo.height > 0, "Height must be over zero"); + NazaraAssert(m_textureInfo.depth > 0, "Depth must be over zero"); + NazaraAssert(m_textureInfo.layerCount == 1, "Array count must be one"); createInfo.imageType = VK_IMAGE_TYPE_3D; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_3D; break; case ImageType::Cubemap: - NazaraAssert(textureInfo.width > 0, "Width must be over zero"); - NazaraAssert(textureInfo.height > 0, "Height must be over zero"); - NazaraAssert(textureInfo.depth == 1, "Depth must be one"); - NazaraAssert(textureInfo.layerCount > 0 && textureInfo.layerCount % 6 == 0, "Array count must be a multiple of 6"); + NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); + NazaraAssert(m_textureInfo.height > 0, "Height must be over zero"); + NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); + NazaraAssert(m_textureInfo.layerCount > 0 && m_textureInfo.layerCount % 6 == 0, "Array count must be a multiple of 6"); createInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; createInfo.imageType = VK_IMAGE_TYPE_2D; @@ -95,11 +100,11 @@ namespace Nz break; } - createInfo.extent.width = textureInfo.width; - createInfo.extent.height = textureInfo.height; - createInfo.extent.depth = textureInfo.depth; - createInfo.arrayLayers = textureInfo.layerCount; - createInfo.mipLevels = textureInfo.levelCount; + createInfo.extent.width = m_textureInfo.width; + createInfo.extent.height = m_textureInfo.height; + createInfo.extent.depth = m_textureInfo.depth; + createInfo.arrayLayers = m_textureInfo.layerCount; + createInfo.mipLevels = m_textureInfo.levelCount; VmaAllocationCreateInfo allocInfo = {}; allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; @@ -111,8 +116,8 @@ namespace Nz CallOnExit releaseImage([&]{ vmaDestroyImage(m_device.GetMemoryAllocator(), m_image, m_allocation); }); // Create default view (viewing the whole texture) - m_imageRange = { - ToVulkan(PixelFormatInfo::GetContent(textureInfo.pixelFormat)), + m_subresourceRange = { + ToVulkan(PixelFormatInfo::GetContent(m_textureInfo.pixelFormat)), 0, //< baseMipLevel createInfo.mipLevels, //< levelCount 0, //< baseArrayLayer @@ -120,7 +125,7 @@ namespace Nz }; createInfoView.image = m_image; - createInfoView.subresourceRange = m_imageRange; + createInfoView.subresourceRange = m_subresourceRange; if (!m_imageView.Create(m_device, createInfoView)) throw std::runtime_error("Failed to create default image view: " + TranslateVulkanError(m_imageView.GetLastErrorCode())); @@ -128,19 +133,91 @@ namespace Nz releaseImage.Reset(); } + VulkanTexture::VulkanTexture(VulkanDevice& device, const TextureInfo& textureInfo, const void* initialData, bool buildMipmaps, unsigned int srcWidth, unsigned int srcHeight) : + VulkanTexture(device, textureInfo) + { + Vk::AutoCommandBuffer initCommandBuffer = m_device.AllocateCommandBuffer(QueueType::Graphics); + if (!initCommandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT)) + throw std::runtime_error("failed to allocate command buffer"); + + initCommandBuffer->SetImageLayout(m_image, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, BuildSubresourceRange(0, m_textureInfo.levelCount, 0, m_textureInfo.layerCount)); + + std::unique_ptr uploadBuffer; + + Boxui wholeRegion(0, 0, 0, m_textureInfo.width, m_textureInfo.height, m_textureInfo.depth); + Image::ArrayToRegion(m_textureInfo.type, 0, m_textureInfo.layerCount, wholeRegion); + + Update(initCommandBuffer, uploadBuffer, initialData, wholeRegion, srcWidth, srcHeight, 0); + + if (buildMipmaps && m_textureInfo.levelCount > 1) + { + Vector3i32 mipSize(SafeCast(m_textureInfo.width), SafeCast(m_textureInfo.height), SafeCast(m_textureInfo.depth)); + Vector3i32 prevMipSize = mipSize; + + for (UInt32 i = 1; i < m_textureInfo.levelCount; ++i) + { + mipSize /= 2; + mipSize.Maximize({ 1, 1, 1 }); + + VkImageBlit blitRegion = { + BuildSubresourceLayers(i - 1), + { //< srcOffsets + { 0, 0, 0 }, + { prevMipSize.x, prevMipSize.y, prevMipSize.z } + }, + BuildSubresourceLayers(i), + { //< dstOffsets + { 0, 0, 0 }, + { mipSize.x, mipSize.y, mipSize.z } + }, + }; + + VkImageSubresourceRange prevMipmapRange = BuildSubresourceRange(i - 1, 1, 0, m_textureInfo.layerCount); + + initCommandBuffer->SetImageLayout(m_image, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, prevMipmapRange); + + initCommandBuffer->BlitImage(m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, blitRegion, VK_FILTER_LINEAR); + + initCommandBuffer->SetImageLayout(m_image, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, prevMipmapRange); + + prevMipSize = mipSize; + } + + VkImageSubresourceRange lastMipmapRange = BuildSubresourceRange(m_textureInfo.levelCount - 1, 1, 0, m_textureInfo.layerCount); + + initCommandBuffer->SetImageLayout(m_image, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, lastMipmapRange); + } + else + { + VkImageSubresourceRange subresourceRange = BuildSubresourceRange(0, m_textureInfo.levelCount, 0, m_textureInfo.layerCount); + + initCommandBuffer->SetImageLayout(m_image, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, subresourceRange); + } + + if (!initCommandBuffer->End()) + throw std::runtime_error("failed to end command buffer"); + + Vk::QueueHandle transferQueue = m_device.GetQueue(m_device.GetDefaultFamilyIndex(QueueType::Graphics), 0); + if (!transferQueue.Submit(initCommandBuffer)) + throw std::runtime_error("failed to submit command buffer"); + + transferQueue.WaitIdle(); + } + VulkanTexture::VulkanTexture(std::shared_ptr parentTexture, const TextureViewInfo& viewInfo) : m_parentTexture(std::move(parentTexture)), + m_device(m_parentTexture->m_device), m_image(m_parentTexture->m_image), m_allocation(nullptr), - m_device(m_parentTexture->m_device) + m_textureInfo(m_parentTexture->m_textureInfo) { - m_textureInfo = ApplyView(m_parentTexture->m_textureInfo, viewInfo); + m_textureViewInfo = ApplyView(m_parentTexture->m_textureViewInfo, viewInfo); - NazaraAssert(viewInfo.layerCount <= m_parentTexture->m_textureInfo.layerCount - viewInfo.baseArrayLayer, "layer count exceeds number of layers"); - NazaraAssert(viewInfo.levelCount <= m_parentTexture->m_textureInfo.levelCount - viewInfo.baseMipLevel, "level count exceeds number of levels"); + NazaraAssert(viewInfo.layerCount <= m_parentTexture->m_textureViewInfo.layerCount - viewInfo.baseArrayLayer, "layer count exceeds number of layers"); + NazaraAssert(viewInfo.levelCount <= m_parentTexture->m_textureViewInfo.levelCount - viewInfo.baseMipLevel, "level count exceeds number of levels"); m_viewInfo = viewInfo; - m_imageRange = { + m_subresourceRange = { ToVulkan(PixelFormatInfo::GetContent(viewInfo.reinterpretFormat)), viewInfo.baseMipLevel, viewInfo.levelCount, @@ -151,60 +228,60 @@ namespace Nz VkImageViewCreateInfo createInfoView = {}; createInfoView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfoView.image = m_image; - createInfoView.subresourceRange = m_imageRange; + createInfoView.subresourceRange = m_subresourceRange; - switch (m_textureInfo.type) + switch (m_textureViewInfo.type) { case ImageType::E1D: - NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); - NazaraAssert(m_textureInfo.height == 1, "Height must be one"); - NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); - NazaraAssert(m_textureInfo.layerCount == 1, "Array count must be one"); + NazaraAssert(m_textureViewInfo.width > 0, "Width must be over zero"); + NazaraAssert(m_textureViewInfo.height == 1, "Height must be one"); + NazaraAssert(m_textureViewInfo.depth == 1, "Depth must be one"); + NazaraAssert(m_textureViewInfo.layerCount == 1, "Array count must be one"); createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D; break; case ImageType::E1D_Array: - NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); - NazaraAssert(m_textureInfo.height == 1, "Height must be one"); - NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); - NazaraAssert(m_textureInfo.layerCount > 0, "Array count must be over zero"); + NazaraAssert(m_textureViewInfo.width > 0, "Width must be over zero"); + NazaraAssert(m_textureViewInfo.height == 1, "Height must be one"); + NazaraAssert(m_textureViewInfo.depth == 1, "Depth must be one"); + NazaraAssert(m_textureViewInfo.layerCount > 0, "Array count must be over zero"); createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY; break; case ImageType::E2D: - NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); - NazaraAssert(m_textureInfo.height > 0, "Height must be over zero"); - NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); - NazaraAssert(m_textureInfo.layerCount == 1, "Array count must be one"); + NazaraAssert(m_textureViewInfo.width > 0, "Width must be over zero"); + NazaraAssert(m_textureViewInfo.height > 0, "Height must be over zero"); + NazaraAssert(m_textureViewInfo.depth == 1, "Depth must be one"); + NazaraAssert(m_textureViewInfo.layerCount == 1, "Array count must be one"); createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D; break; case ImageType::E2D_Array: - NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); - NazaraAssert(m_textureInfo.height > 0, "Height must be over zero"); - NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); - NazaraAssert(m_textureInfo.layerCount > 0, "Array count must be over zero"); + NazaraAssert(m_textureViewInfo.width > 0, "Width must be over zero"); + NazaraAssert(m_textureViewInfo.height > 0, "Height must be over zero"); + NazaraAssert(m_textureViewInfo.depth == 1, "Depth must be one"); + NazaraAssert(m_textureViewInfo.layerCount > 0, "Array count must be over zero"); createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; break; case ImageType::E3D: - NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); - NazaraAssert(m_textureInfo.height > 0, "Height must be over zero"); - NazaraAssert(m_textureInfo.depth > 0, "Depth must be over zero"); - NazaraAssert(m_textureInfo.layerCount == 1, "Array count must be one"); + NazaraAssert(m_textureViewInfo.width > 0, "Width must be over zero"); + NazaraAssert(m_textureViewInfo.height > 0, "Height must be over zero"); + NazaraAssert(m_textureViewInfo.depth > 0, "Depth must be over zero"); + NazaraAssert(m_textureViewInfo.layerCount == 1, "Array count must be one"); createInfoView.viewType = VK_IMAGE_VIEW_TYPE_3D; break; case ImageType::Cubemap: - NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); - NazaraAssert(m_textureInfo.height > 0, "Height must be over zero"); - NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); - NazaraAssert(m_textureInfo.layerCount > 0 && m_textureInfo.layerCount % 6 == 0, "Array count must be a multiple of 6"); + NazaraAssert(m_textureViewInfo.width > 0, "Width must be over zero"); + NazaraAssert(m_textureViewInfo.height > 0, "Height must be over zero"); + NazaraAssert(m_textureViewInfo.depth == 1, "Depth must be one"); + NazaraAssert(m_textureViewInfo.layerCount > 0 && m_textureViewInfo.layerCount % 6 == 0, "Array count must be a multiple of 6"); createInfoView.viewType = VK_IMAGE_VIEW_TYPE_CUBE; break; @@ -296,34 +373,40 @@ namespace Nz bool VulkanTexture::Update(const void* ptr, const Boxui& box, unsigned int srcWidth, unsigned int srcHeight, UInt8 level) { - std::size_t textureSize = box.width * box.height * box.depth * PixelFormatInfo::GetBytesPerPixel(m_textureInfo.pixelFormat); - if (m_textureInfo.type == ImageType::Cubemap) - textureSize *= 6; - - VkBufferCreateInfo createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - createInfo.size = textureSize; - createInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - - VmaAllocationCreateInfo allocInfo = {}; - allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT; - allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; - - VmaAllocationInfo allocationInfo; - - VkBuffer stagingBuffer; - VmaAllocation stagingAllocation; - - VkResult result = vmaCreateBuffer(m_device.GetMemoryAllocator(), &createInfo, &allocInfo, &stagingBuffer, &stagingAllocation, &allocationInfo); - if (result != VK_SUCCESS) - { - NazaraError("Failed to allocate staging buffer: " + TranslateVulkanError(result)); + Vk::AutoCommandBuffer copyCommandBuffer = m_device.AllocateCommandBuffer(QueueType::Graphics); + if (!copyCommandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT)) return false; - } - CallOnExit freeStaging([&] { - vmaDestroyBuffer(m_device.GetMemoryAllocator(), stagingBuffer, stagingAllocation); - }); + unsigned int baseLayer, layerCount; + Image::RegionToArray(m_textureViewInfo.type, box, baseLayer, layerCount); + + VkImageSubresourceRange subresourceRange = BuildSubresourceRange(level, 1, baseLayer, layerCount); + + copyCommandBuffer->SetImageLayout(m_image, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); + + std::unique_ptr uploadBuffer; + Update(copyCommandBuffer, uploadBuffer, ptr, box, srcWidth, srcHeight, level); + + copyCommandBuffer->SetImageLayout(m_image, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, subresourceRange); + + if (!copyCommandBuffer->End()) + return false; + + Vk::QueueHandle transferQueue = m_device.GetQueue(m_device.GetDefaultFamilyIndex(QueueType::Graphics), 0); + if (!transferQueue.Submit(copyCommandBuffer)) + return false; + + transferQueue.WaitIdle(); + + return true; + } + + bool VulkanTexture::Update(Vk::CommandBuffer& commandBuffer, std::unique_ptr& uploadBuffer, const void* ptr, const Boxui& box, unsigned int srcWidth, unsigned int srcHeight, UInt8 level) + { + std::size_t memorySize = box.width * box.height * box.depth * PixelFormatInfo::GetBytesPerPixel(m_textureViewInfo.pixelFormat); + + uploadBuffer = std::make_unique(m_device, BufferType::Upload, memorySize, BufferUsage::DirectMapping); + void* mappedUploadBuffer = uploadBuffer->Map(0, memorySize); if (srcWidth == 0) srcWidth = box.width; @@ -332,13 +415,13 @@ namespace Nz srcHeight = box.height; if (srcWidth == box.width && srcHeight == box.height) - std::memcpy(allocationInfo.pMappedData, ptr, textureSize); + std::memcpy(mappedUploadBuffer, ptr, memorySize); else { unsigned int dstWidth = box.width; unsigned int dstHeight = box.height; - unsigned int bpp = PixelFormatInfo::GetBytesPerPixel(m_textureInfo.pixelFormat); + unsigned int bpp = PixelFormatInfo::GetBytesPerPixel(m_textureViewInfo.pixelFormat); unsigned int lineStride = box.width * bpp; unsigned int dstLineStride = dstWidth * bpp; unsigned int dstFaceStride = dstLineStride * dstHeight; @@ -346,7 +429,7 @@ namespace Nz unsigned int srcFaceStride = srcLineStride * srcHeight; const UInt8* source = static_cast(ptr); - UInt8* destination = static_cast(allocationInfo.pMappedData); + UInt8* destination = static_cast(mappedUploadBuffer); for (unsigned int i = 0; i < box.depth; ++i) { UInt8* dstFacePtr = destination; @@ -364,28 +447,12 @@ namespace Nz } } - Vk::AutoCommandBuffer copyCommandBuffer = m_device.AllocateCommandBuffer(QueueType::Graphics); - if (!copyCommandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT)) - return false; + uploadBuffer->Unmap(); - VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT; - if (PixelFormatInfo::GetContent(m_textureInfo.pixelFormat) == PixelFormatContent::Depth) - aspect = VK_IMAGE_ASPECT_DEPTH_BIT; + unsigned int baseLayer, layerCount; + Boxui copyBox = Image::RegionToArray(m_textureViewInfo.type, box, baseLayer, layerCount); - VkImageSubresourceLayers subresourceLayers = { //< FIXME - aspect, - level, //< mipLevel - m_imageRange.baseArrayLayer, //< baseArrayLayer - UInt32((m_textureInfo.type == ImageType::Cubemap) ? 6 : 1) //< layerCount - }; - - VkImageSubresourceRange subresourceRange = { //< FIXME - aspect, - m_imageRange.baseMipLevel, //< baseMipLevel - 1, //< levelCount - subresourceLayers.baseArrayLayer, //< baseArrayLayer - subresourceLayers.layerCount //< layerCount - }; + VkImageSubresourceLayers subresourceLayers = BuildSubresourceLayers(level, baseLayer, layerCount); VkBufferImageCopy region = { 0, @@ -393,34 +460,24 @@ namespace Nz 0, subresourceLayers, { // imageOffset - SafeCast(box.x), SafeCast(box.y), SafeCast(box.z) + SafeCast(copyBox.x), SafeCast(copyBox.y), SafeCast(copyBox.z) }, { // imageExtent - box.width, box.height, box.depth + copyBox.width, copyBox.height, copyBox.depth } }; - copyCommandBuffer->SetImageLayout(m_image, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); - - copyCommandBuffer->CopyBufferToImage(stagingBuffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region); - - copyCommandBuffer->SetImageLayout(m_image, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, subresourceRange); - - if (!copyCommandBuffer->End()) - return false; - - Vk::QueueHandle transferQueue = m_device.GetQueue(m_device.GetDefaultFamilyIndex(QueueType::Graphics), 0); - if (!transferQueue.Submit(copyCommandBuffer)) - return false; - - transferQueue.WaitIdle(); + commandBuffer.CopyBufferToImage(uploadBuffer->GetBuffer(), m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region); return true; } void VulkanTexture::UpdateDebugName(std::string_view name) { - return m_device.SetDebugName(VK_OBJECT_TYPE_IMAGE, VulkanHandleToInteger(m_image), name); + if (!m_parentTexture) + m_device.SetDebugName(VK_OBJECT_TYPE_IMAGE, VulkanHandleToInteger(m_image), name); + + m_device.SetDebugName(VK_OBJECT_TYPE_IMAGE_VIEW, VulkanHandleToInteger(static_cast(m_imageView)), name); } void VulkanTexture::InitViewForFormat(PixelFormat pixelFormat, VkImageViewCreateInfo& createImageView) diff --git a/src/Nazara/VulkanRenderer/VulkanTextureSampler.cpp b/src/Nazara/VulkanRenderer/VulkanTextureSampler.cpp index 0fac100fb..894dfd296 100644 --- a/src/Nazara/VulkanRenderer/VulkanTextureSampler.cpp +++ b/src/Nazara/VulkanRenderer/VulkanTextureSampler.cpp @@ -24,6 +24,7 @@ namespace Nz createInfo.mipmapMode = ToVulkan(samplerInfo.mipmapMode); createInfo.compareEnable = samplerInfo.depthCompare; createInfo.compareOp = ToVulkan(samplerInfo.depthComparison); + createInfo.maxLod = VK_LOD_CLAMP_NONE; if (samplerInfo.anisotropyLevel > 1.f) {