diff --git a/examples/VulkanTest/main.cpp b/examples/VulkanTest/main.cpp index 864c9f5ed..754911922 100644 --- a/examples/VulkanTest/main.cpp +++ b/examples/VulkanTest/main.cpp @@ -135,132 +135,21 @@ int main() return __LINE__; } - VkImageCreateInfo imageInfo = {}; - imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageInfo.imageType = VK_IMAGE_TYPE_2D; - imageInfo.extent.width = static_cast(drfreakImage->GetWidth()); - imageInfo.extent.height = static_cast(drfreakImage->GetHeight()); - imageInfo.extent.depth = 1; - imageInfo.mipLevels = 1; - imageInfo.arrayLayers = 1; - imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageInfo.format = VK_FORMAT_R8G8B8A8_SRGB; - imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; - imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + Nz::TextureInfo texParams; + texParams.pixelFormat = drfreakImage->GetFormat(); + texParams.type = drfreakImage->GetType(); + texParams.width = drfreakImage->GetWidth(); + texParams.height = drfreakImage->GetHeight(); + texParams.depth = drfreakImage->GetDepth(); - Nz::Vk::Image vkImage; - if (!vkImage.Create(vulkanDevice, imageInfo)) + std::unique_ptr texture = device->InstantiateTexture(texParams); + if (!texture->Update(drfreakImage->GetConstPixels())) { - NazaraError("Failed to create vulkan image"); + NazaraError("Failed to update texture"); return __LINE__; } - VkMemoryRequirements imageMemRequirement = vkImage.GetMemoryRequirements(); - - Nz::Vk::DeviceMemory imageMemory; - if (!imageMemory.Create(vulkanDevice, imageMemRequirement.size, imageMemRequirement.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) - { - NazaraError("Failed to create vulkan image memory"); - return __LINE__; - } - - vkImage.BindImageMemory(imageMemory); - - // Update texture - { - Nz::Vk::Buffer stagingImageBuffer; - if (!stagingImageBuffer.Create(vulkanDevice, 0, drfreakImage->GetMemoryUsage(), VK_BUFFER_USAGE_TRANSFER_SRC_BIT)) - { - NazaraError("Failed to create staging buffer"); - return __LINE__; - } - - VkMemoryPropertyFlags memoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; - - Nz::Vk::DeviceMemory stagingImageMemory; - - VkMemoryRequirements memRequirement = stagingImageBuffer.GetMemoryRequirements(); - if (!stagingImageMemory.Create(vulkanDevice, memRequirement.size, memRequirement.memoryTypeBits, memoryProperties)) - { - NazaraError("Failed to allocate vertex buffer memory"); - return __LINE__; - } - - if (!stagingImageBuffer.BindBufferMemory(stagingImageMemory)) - { - NazaraError("Failed to bind vertex buffer to its memory"); - return __LINE__; - } - - if (!stagingImageMemory.Map(0, memRequirement.size)) - return __LINE__; - - std::memcpy(stagingImageMemory.GetMappedPointer(), drfreakImage->GetPixels(), drfreakImage->GetMemoryUsage()); - - stagingImageMemory.FlushMemory(); - stagingImageMemory.Unmap(); - - Nz::Vk::CommandBuffer copyCommand = cmdPool.AllocateCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY); - copyCommand.Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); - copyCommand.SetImageLayout(vkImage, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - copyCommand.CopyBufferToImage(stagingImageBuffer, vkImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, drfreakImage->GetWidth(), drfreakImage->GetHeight()); - copyCommand.SetImageLayout(vkImage, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - if (!copyCommand.End()) - return __LINE__; - - if (!graphicsQueue.Submit(copyCommand)) - return __LINE__; - - graphicsQueue.WaitIdle(); - } - - // Create image view - - VkImageViewCreateInfo imageViewInfo = {}; - imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - imageViewInfo.components = { - VK_COMPONENT_SWIZZLE_R, - VK_COMPONENT_SWIZZLE_G, - VK_COMPONENT_SWIZZLE_B, - VK_COMPONENT_SWIZZLE_A - }; - imageViewInfo.format = VK_FORMAT_R8G8B8A8_SRGB; - imageViewInfo.image = vkImage; - imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - imageViewInfo.subresourceRange = { - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - 1, - 0, - 1 - }; - - Nz::Vk::ImageView imageView; - if (!imageView.Create(vulkanDevice, imageViewInfo)) - return __LINE__; - - // Sampler - - VkSamplerCreateInfo samplerInfo = {}; - samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - samplerInfo.magFilter = VK_FILTER_LINEAR; - samplerInfo.minFilter = VK_FILTER_LINEAR; - samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerInfo.anisotropyEnable = VK_FALSE; - samplerInfo.maxAnisotropy = 16; - samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; - samplerInfo.unnormalizedCoordinates = VK_FALSE; - samplerInfo.compareEnable = VK_FALSE; - samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; - samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - - Nz::Vk::Sampler imageSampler; - if (!imageSampler.Create(vulkanDevice, samplerInfo)) - return __LINE__; + std::unique_ptr textureSampler = device->InstantiateTextureSampler({}); struct { diff --git a/include/Nazara/Renderer.hpp b/include/Nazara/Renderer.hpp index b043de0de..546ea5c55 100644 --- a/include/Nazara/Renderer.hpp +++ b/include/Nazara/Renderer.hpp @@ -51,5 +51,6 @@ #include #include #include +#include #endif // NAZARA_GLOBAL_RENDERER_HPP diff --git a/include/Nazara/Renderer/RenderDevice.hpp b/include/Nazara/Renderer/RenderDevice.hpp index daf74ca2e..ad48155a4 100644 --- a/include/Nazara/Renderer/RenderDevice.hpp +++ b/include/Nazara/Renderer/RenderDevice.hpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -31,6 +33,8 @@ namespace Nz virtual std::shared_ptr InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) = 0; virtual std::shared_ptr InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) = 0; std::shared_ptr InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const std::filesystem::path& sourcePath); + virtual std::unique_ptr InstantiateTexture(const TextureInfo& params) = 0; + virtual std::unique_ptr InstantiateTextureSampler(const TextureSamplerInfo& params) = 0; }; } diff --git a/include/Nazara/Renderer/Texture.hpp b/include/Nazara/Renderer/Texture.hpp index 7dd8cad0a..4d9776879 100644 --- a/include/Nazara/Renderer/Texture.hpp +++ b/include/Nazara/Renderer/Texture.hpp @@ -8,128 +8,40 @@ #define NAZARA_TEXTURE_HPP #include -#include -#include #include -#include -#include +#include #include -#include -#include -#include +#include namespace Nz { - class Texture; - - using TextureConstRef = ObjectRef; - using TextureLibrary = ObjectLibrary; - using TextureManager = ResourceManager; - using TextureRef = ObjectRef; - - struct TextureImpl; - - class NAZARA_RENDERER_API Texture : public AbstractImage, public Resource + struct TextureInfo { - friend TextureLibrary; - friend TextureManager; - friend class Renderer; + PixelFormatType pixelFormat; + ImageType type; + unsigned int depth = 1; + unsigned int height; + unsigned int mipmapLevel = 1; + unsigned int width; + }; + class NAZARA_RENDERER_API Texture : public Resource + { public: Texture() = default; - Texture(ImageType type, PixelFormatType format, unsigned int width, unsigned int height, unsigned int depth = 1, UInt8 levelCount = 1); Texture(const Texture&) = delete; Texture(Texture&&) = delete; - ~Texture(); + virtual ~Texture(); - bool Create(ImageType type, PixelFormatType format, unsigned int width, unsigned int height, unsigned int depth = 1, UInt8 levelCount = 1); - void Destroy(); + virtual PixelFormatType GetFormat() const = 0; + virtual UInt8 GetLevelCount() const = 0; + virtual Vector3ui GetSize(UInt8 level = 0) const = 0; + virtual ImageType GetType() const = 0; - bool Download(Image* image) const; - - bool EnableMipmapping(bool enable); - - void EnsureMipmapsUpdate() const; - - unsigned int GetDepth(UInt8 level = 0) const override; - PixelFormatType GetFormat() const override; - unsigned int GetHeight(UInt8 level = 0) const override; - UInt8 GetLevelCount() const override; - UInt8 GetMaxLevel() const override; - std::size_t GetMemoryUsage() const override; - std::size_t GetMemoryUsage(UInt8 level) const override; - Vector3ui GetSize(UInt8 level = 0) const override; - ImageType GetType() const override; - unsigned int GetWidth(UInt8 level = 0) const override; - - bool HasMipmaps() const; - - void InvalidateMipmaps(); - bool IsValid() const; - - // LoadFace - bool LoadFaceFromFile(CubemapFace face, const std::filesystem::path& filePath, const ImageParams& params = ImageParams()); - bool LoadFaceFromMemory(CubemapFace face, const void* data, std::size_t size, const ImageParams& params = ImageParams()); - bool LoadFaceFromStream(CubemapFace face, Stream& stream, const ImageParams& params = ImageParams()); - - // Save - bool SaveToFile(const std::filesystem::path& filePath, const ImageParams& params = ImageParams()); - bool SaveToStream(Stream& stream, const std::string& format, const ImageParams& params = ImageParams()); - - bool SetMipmapRange(UInt8 minLevel, UInt8 maxLevel); - - bool Update(const Image* image, UInt8 level = 0); - bool Update(const Image* image, const Boxui& box, UInt8 level = 0); - bool Update(const Image* image, const Rectui& rect, unsigned int z = 0, UInt8 level = 0); - bool Update(const UInt8* pixels, unsigned int srcWidth = 0, unsigned int srcHeight = 0, UInt8 level = 0) override; - bool Update(const UInt8* pixels, const Boxui& box, unsigned int srcWidth = 0, unsigned int srcHeight = 0, UInt8 level = 0) override; - bool Update(const UInt8* pixels, const Rectui& rect, unsigned int z = 0, unsigned int srcWidth = 0, unsigned int srcHeight = 0, UInt8 level = 0) override; - - // Fonctions OpenGL - unsigned int GetOpenGLID() const; + virtual bool Update(const void* ptr) = 0; Texture& operator=(const Texture&) = delete; Texture& operator=(Texture&&) = delete; - - static bool IsFormatSupported(PixelFormatType format); - static bool IsMipmappingSupported(); - static bool IsTypeSupported(ImageType type); - - // Load - static TextureRef LoadFromFile(const std::filesystem::path& filePath, const ImageParams& params = ImageParams(), bool generateMipmaps = true); - static TextureRef LoadFromImage(const Image* image, bool generateMipmaps = true); - static TextureRef LoadFromMemory(const void* data, std::size_t size, const ImageParams& params = ImageParams(), bool generateMipmaps = true); - static TextureRef LoadFromStream(Stream& stream, const ImageParams& params = ImageParams(), bool generateMipmaps = true); - - // LoadArray - static TextureRef LoadArrayFromFile(const std::filesystem::path& filePath, const ImageParams& imageParams = ImageParams(), bool generateMipmaps = true, const Vector2ui& atlasSize = Vector2ui(2, 2)); - static TextureRef LoadArrayFromImage(const Image* image, bool generateMipmaps = true, const Vector2ui& atlasSize = Vector2ui(2, 2)); - static TextureRef LoadArrayFromMemory(const void* data, std::size_t size, const ImageParams& imageParams = ImageParams(), bool generateMipmaps = true, const Vector2ui& atlasSize = Vector2ui(2, 2)); - static TextureRef LoadArrayFromStream(Stream& stream, const ImageParams& imageParams = ImageParams(), bool generateMipmaps = true, const Vector2ui& atlasSize = Vector2ui(2, 2)); - - // LoadCubemap - static TextureRef LoadCubemapFromFile(const std::filesystem::path& filePath, const ImageParams& imageParams = ImageParams(), bool generateMipmaps = true, const CubemapParams& cubemapParams = CubemapParams()); - static TextureRef LoadCubemapFromImage(const Image* image, bool generateMipmaps = true, const CubemapParams& params = CubemapParams()); - static TextureRef LoadCubemapFromMemory(const void* data, std::size_t size, const ImageParams& imageParams = ImageParams(), bool generateMipmaps = true, const CubemapParams& cubemapParams = CubemapParams()); - static TextureRef LoadCubemapFromStream(Stream& stream, const ImageParams& imageParams = ImageParams(), bool generateMipmaps = true, const CubemapParams& cubemapParams = CubemapParams()); - - template static TextureRef New(Args&&... args); - - // Signals: - NazaraSignal(OnTextureDestroy, const Texture* /*texture*/); - NazaraSignal(OnTextureRelease, const Texture* /*texture*/); - - private: - bool CreateTexture(bool proxy); - - static bool Initialize(); - static void Uninitialize(); - - TextureImpl* m_impl = nullptr; - - static TextureLibrary::LibraryMap s_library; - static TextureManager::ManagerMap s_managerMap; - static TextureManager::ManagerParams s_managerParameters; }; } diff --git a/include/Nazara/Renderer/Texture.inl b/include/Nazara/Renderer/Texture.inl new file mode 100644 index 000000000..f389a6984 --- /dev/null +++ b/include/Nazara/Renderer/Texture.inl @@ -0,0 +1,12 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ +} + +#include diff --git a/include/Nazara/Renderer/TextureSampler.hpp b/include/Nazara/Renderer/TextureSampler.hpp new file mode 100644 index 000000000..e050298c3 --- /dev/null +++ b/include/Nazara/Renderer/TextureSampler.hpp @@ -0,0 +1,42 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_TEXTURE_SAMPLER_HPP +#define NAZARA_TEXTURE_SAMPLER_HPP + +#include +#include +#include + +namespace Nz +{ + struct TextureSamplerInfo + { + float anisotropyLevel = 0.f; + SamplerFilter magFilter = SamplerFilter_Linear; + SamplerFilter minFilter = SamplerFilter_Linear; + SamplerMipmapMode mipmapMode = SamplerMipmapMode_Linear; + SamplerWrap wrapModeU = SamplerWrap_Clamp; + SamplerWrap wrapModeV = SamplerWrap_Clamp; + SamplerWrap wrapModeW = SamplerWrap_Clamp; + }; + + class NAZARA_RENDERER_API TextureSampler + { + public: + TextureSampler() = default; + TextureSampler(const TextureSampler&) = delete; + TextureSampler(TextureSampler&&) = delete; + virtual ~TextureSampler(); + + TextureSampler& operator=(const TextureSampler&) = delete; + TextureSampler& operator=(TextureSampler&&) = delete; + }; +} + +#include + +#endif // NAZARA_TEXTURE_HPP diff --git a/include/Nazara/Renderer/TextureSampler.inl b/include/Nazara/Renderer/TextureSampler.inl new file mode 100644 index 000000000..a741958f0 --- /dev/null +++ b/include/Nazara/Renderer/TextureSampler.inl @@ -0,0 +1,12 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ +} + +#include diff --git a/include/Nazara/Utility/Enums.hpp b/include/Nazara/Utility/Enums.hpp index 9c7a23554..602c60f0f 100644 --- a/include/Nazara/Utility/Enums.hpp +++ b/include/Nazara/Utility/Enums.hpp @@ -286,27 +286,26 @@ namespace Nz enum SamplerFilter { - SamplerFilter_Unknown = -1, - - SamplerFilter_Bilinear, + SamplerFilter_Linear, SamplerFilter_Nearest, - SamplerFilter_Trilinear, - SamplerFilter_Default, + SamplerFilter_Max = SamplerFilter_Nearest + }; - SamplerFilter_Max = SamplerFilter_Default + enum SamplerMipmapMode + { + SamplerMipmapMode_Linear, + SamplerMipmapMode_Nearest, + + SamplerMipmapMode_Max = SamplerMipmapMode_Nearest }; enum SamplerWrap { - SamplerWrap_Unknown = -1, - SamplerWrap_Clamp, SamplerWrap_MirroredRepeat, SamplerWrap_Repeat, - SamplerWrap_Default, - SamplerWrap_Max = SamplerWrap_Repeat }; diff --git a/include/Nazara/VulkanRenderer.hpp b/include/Nazara/VulkanRenderer.hpp index 3b21f92c2..5aa12a1c7 100644 --- a/include/Nazara/VulkanRenderer.hpp +++ b/include/Nazara/VulkanRenderer.hpp @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #include diff --git a/include/Nazara/VulkanRenderer/Utils.hpp b/include/Nazara/VulkanRenderer/Utils.hpp index 3d772abca..173589965 100644 --- a/include/Nazara/VulkanRenderer/Utils.hpp +++ b/include/Nazara/VulkanRenderer/Utils.hpp @@ -22,6 +22,9 @@ namespace Nz inline VkPolygonMode ToVulkan(FaceFilling faceFilling); inline VkPrimitiveTopology ToVulkan(PrimitiveMode primitiveMode); inline VkCompareOp ToVulkan(RendererComparison comparison); + inline VkFilter ToVulkan(SamplerFilter samplerFilter); + inline VkSamplerMipmapMode ToVulkan(SamplerMipmapMode samplerMipmap); + inline VkSamplerAddressMode ToVulkan(SamplerWrap samplerWrap); inline VkDescriptorType ToVulkan(ShaderBindingType bindingType); inline VkShaderStageFlagBits ToVulkan(ShaderStageType stageType); inline VkShaderStageFlags ToVulkan(ShaderStageTypeFlags stageType); diff --git a/include/Nazara/VulkanRenderer/Utils.inl b/include/Nazara/VulkanRenderer/Utils.inl index aa0f18d38..a843b67f5 100644 --- a/include/Nazara/VulkanRenderer/Utils.inl +++ b/include/Nazara/VulkanRenderer/Utils.inl @@ -108,6 +108,43 @@ namespace Nz return VK_COMPARE_OP_NEVER; } + VkFilter ToVulkan(SamplerFilter samplerFilter) + { + switch (samplerFilter) + { + case SamplerFilter_Linear: return VK_FILTER_LINEAR; + case SamplerFilter_Nearest: return VK_FILTER_NEAREST; + } + + NazaraError("Unhandled SamplerFilter 0x" + String::Number(UnderlyingCast(samplerFilter), 16)); + return VK_FILTER_NEAREST; + } + + VkSamplerMipmapMode ToVulkan(SamplerMipmapMode samplerMipmap) + { + switch (samplerMipmap) + { + case SamplerMipmapMode_Linear: return VK_SAMPLER_MIPMAP_MODE_LINEAR; + case SamplerMipmapMode_Nearest: return VK_SAMPLER_MIPMAP_MODE_NEAREST; + } + + NazaraError("Unhandled SamplerMipmapMode 0x" + String::Number(UnderlyingCast(samplerMipmap), 16)); + return VK_SAMPLER_MIPMAP_MODE_NEAREST; + } + + VkSamplerAddressMode ToVulkan(SamplerWrap samplerWrap) + { + switch (samplerWrap) + { + case SamplerWrap_Clamp: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + case SamplerWrap_MirroredRepeat: return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; + case SamplerWrap_Repeat: return VK_SAMPLER_ADDRESS_MODE_REPEAT; + } + + NazaraError("Unhandled SamplerWrap 0x" + String::Number(UnderlyingCast(samplerWrap), 16)); + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + } + VkDescriptorType ToVulkan(ShaderBindingType bindingType) { switch (bindingType) diff --git a/include/Nazara/VulkanRenderer/VulkanDevice.hpp b/include/Nazara/VulkanRenderer/VulkanDevice.hpp index fb4a11c79..ce717c414 100644 --- a/include/Nazara/VulkanRenderer/VulkanDevice.hpp +++ b/include/Nazara/VulkanRenderer/VulkanDevice.hpp @@ -27,6 +27,8 @@ namespace Nz std::unique_ptr InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) override; std::shared_ptr InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo) override; std::shared_ptr InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize) override; + std::unique_ptr InstantiateTexture(const TextureInfo& params) override; + std::unique_ptr InstantiateTextureSampler(const TextureSamplerInfo& params) override; VulkanDevice& operator=(const VulkanDevice&) = delete; VulkanDevice& operator=(VulkanDevice&&) = delete; ///TODO? diff --git a/include/Nazara/VulkanRenderer/VulkanTexture.hpp b/include/Nazara/VulkanRenderer/VulkanTexture.hpp new file mode 100644 index 000000000..e5fc61426 --- /dev/null +++ b/include/Nazara/VulkanRenderer/VulkanTexture.hpp @@ -0,0 +1,51 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKANRENDERER_VULKANTEXTURE_HPP +#define NAZARA_VULKANRENDERER_VULKANTEXTURE_HPP + +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_VULKANRENDERER_API VulkanTexture : public Texture + { + public: + VulkanTexture(Vk::Device& device, const TextureInfo& params); + VulkanTexture(const VulkanTexture&) = default; + VulkanTexture(VulkanTexture&&) noexcept = default; + ~VulkanTexture(); + + PixelFormatType GetFormat() const override; + inline VkImage GetImage() const; + inline VkImageView GetImageView() const; + UInt8 GetLevelCount() const override; + Vector3ui GetSize(UInt8 level = 0) const override; + ImageType GetType() const override; + + bool Update(const void* ptr) override; + + VulkanTexture& operator=(const VulkanTexture&) = delete; + VulkanTexture& operator=(VulkanTexture&&) = delete; + + private: + static void InitForFormat(PixelFormatType pixelFormat, VkImageCreateInfo& createImage, VkImageViewCreateInfo& createImageView); + + VkImage m_image; + VmaAllocation m_allocation; + Vk::Device& m_device; + Vk::ImageView m_imageView; + TextureInfo m_params; + }; +} + +#include + +#endif // NAZARA_VULKANRENDERER_VULKANTEXTURE_HPP diff --git a/include/Nazara/VulkanRenderer/VulkanTexture.inl b/include/Nazara/VulkanRenderer/VulkanTexture.inl new file mode 100644 index 000000000..26a36793a --- /dev/null +++ b/include/Nazara/VulkanRenderer/VulkanTexture.inl @@ -0,0 +1,21 @@ +// Copyright (C) 2020 Jérôme Leclercq +// 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 VkImage VulkanTexture::GetImage() const + { + return m_image; + } + + inline VkImageView VulkanTexture::GetImageView() const + { + return m_imageView; + } +} + +#include diff --git a/include/Nazara/VulkanRenderer/VulkanTextureSampler.hpp b/include/Nazara/VulkanRenderer/VulkanTextureSampler.hpp new file mode 100644 index 000000000..2ea6e549b --- /dev/null +++ b/include/Nazara/VulkanRenderer/VulkanTextureSampler.hpp @@ -0,0 +1,36 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKANRENDERER_VULKANTEXTURESAMPLER_HPP +#define NAZARA_VULKANRENDERER_VULKANTEXTURESAMPLER_HPP + +#include +#include +#include + +namespace Nz +{ + class NAZARA_VULKANRENDERER_API VulkanTextureSampler : public TextureSampler + { + public: + VulkanTextureSampler(Vk::Device& device, TextureSamplerInfo samplerInfo); + VulkanTextureSampler(const VulkanTextureSampler&) = default; + VulkanTextureSampler(VulkanTextureSampler&&) noexcept = default; + ~VulkanTextureSampler() = default; + + inline VkSampler GetSampler() const; + + VulkanTextureSampler& operator=(const VulkanTextureSampler&) = delete; + VulkanTextureSampler& operator=(VulkanTextureSampler&&) = delete; + + private: + Vk::Sampler m_sampler; + }; +} + +#include + +#endif // NAZARA_VULKANRENDERER_VULKANTEXTURESAMPLER_HPP diff --git a/include/Nazara/VulkanRenderer/VulkanTextureSampler.inl b/include/Nazara/VulkanRenderer/VulkanTextureSampler.inl new file mode 100644 index 000000000..710f4ee73 --- /dev/null +++ b/include/Nazara/VulkanRenderer/VulkanTextureSampler.inl @@ -0,0 +1,16 @@ +// Copyright (C) 2020 Jérôme Leclercq +// 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 VkSampler VulkanTextureSampler::GetSampler() const + { + return m_sampler; + } +} + +#include diff --git a/src/Nazara/Renderer/Texture.cpp b/src/Nazara/Renderer/Texture.cpp new file mode 100644 index 000000000..6a3e4b1fb --- /dev/null +++ b/src/Nazara/Renderer/Texture.cpp @@ -0,0 +1,11 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + Texture::~Texture() = default; +} diff --git a/src/Nazara/Renderer/TextureSampler.cpp b/src/Nazara/Renderer/TextureSampler.cpp new file mode 100644 index 000000000..e8571e775 --- /dev/null +++ b/src/Nazara/Renderer/TextureSampler.cpp @@ -0,0 +1,11 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + TextureSampler::~TextureSampler() = default; +} diff --git a/src/Nazara/VulkanRenderer/VulkanDevice.cpp b/src/Nazara/VulkanRenderer/VulkanDevice.cpp index 252d7436b..0f8ce9a9f 100644 --- a/src/Nazara/VulkanRenderer/VulkanDevice.cpp +++ b/src/Nazara/VulkanRenderer/VulkanDevice.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include namespace Nz @@ -39,4 +41,14 @@ namespace Nz return stage; } + + std::unique_ptr VulkanDevice::InstantiateTexture(const TextureInfo& params) + { + return std::make_unique(*this, params); + } + + std::unique_ptr VulkanDevice::InstantiateTextureSampler(const TextureSamplerInfo& params) + { + return std::make_unique(*this, params); + } } diff --git a/src/Nazara/VulkanRenderer/VulkanTexture.cpp b/src/Nazara/VulkanRenderer/VulkanTexture.cpp new file mode 100644 index 000000000..f991051d3 --- /dev/null +++ b/src/Nazara/VulkanRenderer/VulkanTexture.cpp @@ -0,0 +1,287 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan Renderer" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + namespace + { + inline unsigned int GetLevelSize(unsigned int size, UInt8 level) + { + if (size == 0) // Possible dans le cas d'une image invalide + return 0; + + return std::max(size >> level, 1U); + } + } + + VulkanTexture::VulkanTexture(Vk::Device& device, const TextureInfo& params) : + m_image(VK_NULL_HANDLE), + m_allocation(nullptr), + m_device(device), + m_params(params) + { + VkImageCreateInfo createInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; + createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + createInfo.mipLevels = params.mipmapLevel; + createInfo.samples = VK_SAMPLE_COUNT_1_BIT; + createInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + createInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + + VkImageViewCreateInfo createInfoView = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + createInfoView.subresourceRange = { + VK_IMAGE_ASPECT_COLOR_BIT, + 0, + 1, + 0, + 1 + }; + + InitForFormat(params.pixelFormat, createInfo, createInfoView); + + switch (params.type) + { + case ImageType_1D: + NazaraAssert(params.width > 0, "Width must be over zero"); + + createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D; + + createInfo.imageType = VK_IMAGE_TYPE_1D; + createInfo.extent.width = params.width; + createInfo.extent.height = 1; + createInfo.extent.depth = 1; + createInfo.arrayLayers = 1; + break; + + case ImageType_1D_Array: + NazaraAssert(params.width > 0, "Width must be over zero"); + NazaraAssert(params.height > 0, "Height must be over zero"); + + createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY; + + createInfo.imageType = VK_IMAGE_TYPE_1D; + createInfo.extent.width = params.width; + createInfo.extent.height = 1; + createInfo.extent.depth = 1; + createInfo.arrayLayers = params.height; + break; + + case ImageType_2D: + NazaraAssert(params.width > 0, "Width must be over zero"); + NazaraAssert(params.height > 0, "Height must be over zero"); + + createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D; + + createInfo.imageType = VK_IMAGE_TYPE_2D; + createInfo.extent.width = params.width; + createInfo.extent.height = params.height; + createInfo.extent.depth = 1; + createInfo.arrayLayers = 1; + break; + + case ImageType_2D_Array: + NazaraAssert(params.width > 0, "Width must be over zero"); + NazaraAssert(params.height > 0, "Height must be over zero"); + NazaraAssert(params.depth > 0, "Depth must be over zero"); + + createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + + createInfo.imageType = VK_IMAGE_TYPE_2D; + createInfo.extent.width = params.width; + createInfo.extent.height = params.height; + createInfo.extent.depth = 1; + createInfo.arrayLayers = params.height; + break; + + case ImageType_3D: + NazaraAssert(params.width > 0, "Width must be over zero"); + NazaraAssert(params.height > 0, "Height must be over zero"); + NazaraAssert(params.depth > 0, "Depth must be over zero"); + + createInfoView.viewType = VK_IMAGE_VIEW_TYPE_3D; + + createInfo.imageType = VK_IMAGE_TYPE_3D; + createInfo.extent.width = params.width; + createInfo.extent.height = params.height; + createInfo.extent.depth = params.depth; + createInfo.arrayLayers = 1; + break; + + case ImageType_Cubemap: + NazaraAssert(params.width > 0, "Width must be over zero"); + NazaraAssert(params.height > 0, "Height must be over zero"); + + createInfoView.viewType = VK_IMAGE_VIEW_TYPE_CUBE; + + createInfo.imageType = VK_IMAGE_TYPE_2D; + createInfo.extent.width = params.width; + createInfo.extent.height = params.height; + createInfo.extent.depth = 1; + createInfo.arrayLayers = 6; + createInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + break; + + default: + break; + } + + VmaAllocationCreateInfo allocInfo = {}; + allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + + VkResult result = vmaCreateImage(m_device.GetMemoryAllocator(), &createInfo, &allocInfo, &m_image, &m_allocation, nullptr); + if (result != VK_SUCCESS) + throw std::runtime_error("Failed to allocate image: " + TranslateVulkanError(result)); + + createInfoView.image = m_image; + + if (!m_imageView.Create(device, createInfoView)) + { + // FIXME + vmaDestroyImage(m_device.GetMemoryAllocator(), m_image, m_allocation); + throw std::runtime_error("Failed to create image view: " + TranslateVulkanError(m_imageView.GetLastErrorCode())); + } + } + + VulkanTexture::~VulkanTexture() + { + vmaDestroyImage(m_device.GetMemoryAllocator(), m_image, m_allocation); + } + + PixelFormatType VulkanTexture::GetFormat() const + { + return m_params.pixelFormat; + } + + UInt8 VulkanTexture::GetLevelCount() const + { + return m_params.mipmapLevel; + } + + Vector3ui VulkanTexture::GetSize(UInt8 level) const + { + return Vector3ui(GetLevelSize(m_params.width, level), GetLevelSize(m_params.height, level), GetLevelSize(m_params.depth, level)); + } + + ImageType VulkanTexture::GetType() const + { + return m_params.type; + } + + bool VulkanTexture::Update(const void* ptr) + { + std::size_t textureSize = m_params.width * m_params.height * m_params.depth * PixelFormat::GetBytesPerPixel(m_params.pixelFormat); + + 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)); + return false; + } + + CallOnExit freeStaging([&] { + vmaDestroyBuffer(m_device.GetMemoryAllocator(), stagingBuffer, stagingAllocation); + }); + + std::memcpy(allocationInfo.pMappedData, ptr, textureSize); + + Vk::AutoCommandBuffer copyCommandBuffer = m_device.AllocateTransferCommandBuffer(); + if (!copyCommandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT)) + return false; + + 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); + + copyCommandBuffer->CopyBufferToImage(stagingBuffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, m_params.width, m_params.height, m_params.depth); + + 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); + + if (!copyCommandBuffer->End()) + return false; + + Vk::QueueHandle transferQueue = m_device.GetQueue(m_device.GetTransferQueueFamilyIndex(), 0); + if (!transferQueue.Submit(copyCommandBuffer)) + return false; + + transferQueue.WaitIdle(); + + return true; + } + + void VulkanTexture::InitForFormat(PixelFormatType pixelFormat, VkImageCreateInfo& createImage, VkImageViewCreateInfo& createImageView) + { + createImageView.components = { + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A + }; + + switch (pixelFormat) + { + case PixelFormatType_L8: + { + createImage.format = VK_FORMAT_R8_SRGB; + createImageView.format = createImage.format; + createImageView.components = { + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_A + }; + break; + } + + case PixelFormatType_LA8: + { + createImage.format = VK_FORMAT_R8G8_SRGB; + createImageView.format = createImage.format; + createImageView.components = { + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G + }; + break; + } + + case PixelFormatType_RGB8: + { + createImage.format = VK_FORMAT_R8G8B8_SRGB; + createImageView.format = createImage.format; + break; + } + + case PixelFormatType_RGBA8: + { + createImage.format = VK_FORMAT_R8G8B8A8_SRGB; + createImageView.format = createImage.format; + break; + } + + default: + throw std::runtime_error(("Unsupported pixel format " + PixelFormat::GetName(pixelFormat)).ToStdString()); + } + } +} diff --git a/src/Nazara/VulkanRenderer/VulkanTextureSampler.cpp b/src/Nazara/VulkanRenderer/VulkanTextureSampler.cpp new file mode 100644 index 000000000..053683bd4 --- /dev/null +++ b/src/Nazara/VulkanRenderer/VulkanTextureSampler.cpp @@ -0,0 +1,31 @@ +// Copyright (C) 2020 Jérôme Leclercq +// This file is part of the "Nazara Engine - Vulkan Renderer" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + VulkanTextureSampler::VulkanTextureSampler(Vk::Device& device, TextureSamplerInfo samplerInfo) + { + VkSamplerCreateInfo createInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + createInfo.magFilter = ToVulkan(samplerInfo.magFilter); + createInfo.minFilter = ToVulkan(samplerInfo.minFilter); + createInfo.addressModeU = ToVulkan(samplerInfo.wrapModeU); + createInfo.addressModeV = ToVulkan(samplerInfo.wrapModeV); + createInfo.addressModeW = ToVulkan(samplerInfo.wrapModeW); + createInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + createInfo.mipmapMode = ToVulkan(samplerInfo.mipmapMode); + + if (samplerInfo.anisotropyLevel > 0.f) + { + createInfo.anisotropyEnable = VK_TRUE; + createInfo.maxAnisotropy = samplerInfo.anisotropyLevel; + } + + if (!m_sampler.Create(device, createInfo)) + throw std::runtime_error("Failed to create sampler: " + TranslateVulkanError(m_sampler.GetLastErrorCode())); + } +}