Renderer: Add mipmaps generation support

This commit is contained in:
SirLynix 2023-05-14 18:55:41 +02:00
parent 3712b641f8
commit 1d32af53c5
33 changed files with 488 additions and 183 deletions

View File

@ -8,7 +8,6 @@
#define NAZARA_OPENGLRENDERER_OPENGLCOMMANDBUFFER_HPP #define NAZARA_OPENGLRENDERER_OPENGLCOMMANDBUFFER_HPP
#include <NazaraUtils/Prerequisites.hpp> #include <NazaraUtils/Prerequisites.hpp>
#include <NazaraUtils/TypeList.hpp>
#include <Nazara/Core/Color.hpp> #include <Nazara/Core/Color.hpp>
#include <Nazara/Math/Rect.hpp> #include <Nazara/Math/Rect.hpp>
#include <Nazara/OpenGLRenderer/Config.hpp> #include <Nazara/OpenGLRenderer/Config.hpp>
@ -17,6 +16,7 @@
#include <Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp> #include <Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp>
#include <Nazara/Renderer/CommandBuffer.hpp> #include <Nazara/Renderer/CommandBuffer.hpp>
#include <Nazara/Renderer/CommandBufferBuilder.hpp> #include <Nazara/Renderer/CommandBufferBuilder.hpp>
#include <NazaraUtils/TypeList.hpp>
#include <optional> #include <optional>
#include <variant> #include <variant>
#include <vector> #include <vector>
@ -46,8 +46,11 @@ namespace Nz
inline void BindRenderPipeline(const OpenGLRenderPipeline* pipeline); inline void BindRenderPipeline(const OpenGLRenderPipeline* pipeline);
inline void BindRenderShaderBinding(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 set, const OpenGLShaderBinding* binding); inline void BindRenderShaderBinding(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 set, const OpenGLShaderBinding* binding);
inline void BindVertexBuffer(UInt32 binding, GLuint vertexBuffer, UInt64 offset = 0); 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 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(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 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); 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) \ #define NAZARA_OPENGL_FOREACH_COMMANDS(cb, lastCb) \
cb(BeginDebugRegionCommand) \ cb(BeginDebugRegionCommand) \
cb(BlitTextureCommand) \ cb(BlitTextureCommand) \
cb(BuildTextureMipmapsCommand) \
cb(CopyBufferCommand) \ cb(CopyBufferCommand) \
cb(CopyBufferFromMemoryCommand) \ cb(CopyBufferFromMemoryCommand) \
cb(CopyTextureCommand) \ 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 BeginDebugRegionCommand& command);
inline void Execute(const GL::Context* context, const BlitTextureCommand& 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 CopyBufferCommand& command);
inline void Execute(const GL::Context* context, const CopyBufferFromMemoryCommand& command); inline void Execute(const GL::Context* context, const CopyBufferFromMemoryCommand& command);
inline void Execute(const GL::Context* context, const CopyTextureCommand& command); inline void Execute(const GL::Context* context, const CopyTextureCommand& command);
@ -139,6 +144,13 @@ namespace Nz
SamplerFilter filter; SamplerFilter filter;
}; };
struct BuildTextureMipmapsCommand
{
OpenGLTexture* texture;
UInt8 baseLevel;
UInt8 levelCount;
};
struct ComputeStates struct ComputeStates
{ {
const OpenGLComputePipeline* pipeline = nullptr; const OpenGLComputePipeline* pipeline = nullptr;

View File

@ -88,6 +88,17 @@ namespace Nz
m_commands.emplace_back(std::move(blitTexture)); 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) inline void OpenGLCommandBuffer::CopyBuffer(GLuint source, GLuint target, UInt64 size, UInt64 sourceOffset, UInt64 targetOffset)
{ {
CopyBufferCommand copyBuffer = { CopyBufferCommand copyBuffer = {

View File

@ -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 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 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 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; void CopyTexture(const Texture& fromTexture, const Boxui& fromBox, TextureLayout fromLayout, const Texture& toTexture, const Vector3ui& toPos, TextureLayout toLayout) override;

View File

@ -47,6 +47,7 @@ namespace Nz
std::shared_ptr<ShaderModule> InstantiateShaderModule(nzsl::ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const nzsl::ShaderWriter::States& states) override; std::shared_ptr<ShaderModule> InstantiateShaderModule(nzsl::ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const nzsl::ShaderWriter::States& states) override;
std::shared_ptr<Swapchain> InstantiateSwapchain(WindowHandle windowHandle, const Vector2ui& windowSize, const SwapchainParameters& parameters) override; std::shared_ptr<Swapchain> InstantiateSwapchain(WindowHandle windowHandle, const Vector2ui& windowSize, const SwapchainParameters& parameters) override;
std::shared_ptr<Texture> InstantiateTexture(const TextureInfo& params) override; std::shared_ptr<Texture> InstantiateTexture(const TextureInfo& params) override;
std::shared_ptr<Texture> InstantiateTexture(const TextureInfo& params, const void* initialData, bool buildMipmaps, unsigned int srcWidth = 0, unsigned int srcHeight = 0) override;
std::shared_ptr<TextureSampler> InstantiateTextureSampler(const TextureSamplerInfo& params) override; std::shared_ptr<TextureSampler> InstantiateTextureSampler(const TextureSamplerInfo& params) override;
bool IsTextureFormatSupported(PixelFormat format, TextureUsage usage) const override; bool IsTextureFormatSupported(PixelFormat format, TextureUsage usage) const override;

View File

@ -20,6 +20,7 @@ namespace Nz
{ {
public: public:
OpenGLTexture(OpenGLDevice& device, const TextureInfo& textureInfo); 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<OpenGLTexture> parentTexture, const TextureViewInfo& viewInfo); OpenGLTexture(std::shared_ptr<OpenGLTexture> parentTexture, const TextureViewInfo& viewInfo);
OpenGLTexture(const OpenGLTexture&) = delete; OpenGLTexture(const OpenGLTexture&) = delete;
OpenGLTexture(OpenGLTexture&&) = delete; OpenGLTexture(OpenGLTexture&&) = delete;
@ -28,6 +29,8 @@ namespace Nz
bool Copy(const Texture& source, const Boxui& srcBox, const Vector3ui& dstPos) override; bool Copy(const Texture& source, const Boxui& srcBox, const Vector3ui& dstPos) override;
std::shared_ptr<Texture> CreateView(const TextureViewInfo& viewInfo) override; std::shared_ptr<Texture> CreateView(const TextureViewInfo& viewInfo) override;
inline void GenerateMipmaps(UInt8 baseLevel, UInt8 levelCount);
inline PixelFormat GetFormat() const override; inline PixelFormat GetFormat() const override;
inline UInt8 GetLevelCount() const override; inline UInt8 GetLevelCount() const override;
inline OpenGLTexture* GetParentTexture() const override; inline OpenGLTexture* GetParentTexture() const override;

View File

@ -2,11 +2,42 @@
// This file is part of the "Nazara Engine - OpenGL renderer" // This file is part of the "Nazara Engine - OpenGL renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp // For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Core/Error.hpp>
#include <stdexcept> #include <stdexcept>
#include <Nazara/OpenGLRenderer/Debug.hpp> #include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz 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 inline PixelFormat OpenGLTexture::GetFormat() const
{ {
return m_textureInfo.pixelFormat; return m_textureInfo.pixelFormat;

View File

@ -24,6 +24,8 @@ namespace Nz::GL
Texture(Texture&&) noexcept = default; Texture(Texture&&) noexcept = default;
~Texture() = default; ~Texture() = default;
inline void GenerateMipmap();
inline TextureTarget GetTarget() const; inline TextureTarget GetTarget() const;
inline void SetParameterf(GLenum pname, GLfloat param); inline void SetParameterf(GLenum pname, GLfloat param);

View File

@ -7,6 +7,13 @@
namespace Nz::GL 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 inline TextureTarget Texture::GetTarget() const
{ {
return m_target; return m_target;

View File

@ -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 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); 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; 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); inline void CopyBuffer(const UploadPool::Allocation& allocation, const RenderBufferView& target);

View File

@ -53,6 +53,7 @@ namespace Nz
std::shared_ptr<ShaderModule> InstantiateShaderModule(nzsl::ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const std::filesystem::path& sourcePath, const nzsl::ShaderWriter::States& states); std::shared_ptr<ShaderModule> InstantiateShaderModule(nzsl::ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const std::filesystem::path& sourcePath, const nzsl::ShaderWriter::States& states);
virtual std::shared_ptr<Swapchain> InstantiateSwapchain(WindowHandle windowHandle, const Vector2ui& windowSize, const SwapchainParameters& parameters) = 0; virtual std::shared_ptr<Swapchain> InstantiateSwapchain(WindowHandle windowHandle, const Vector2ui& windowSize, const SwapchainParameters& parameters) = 0;
virtual std::shared_ptr<Texture> InstantiateTexture(const TextureInfo& params) = 0; virtual std::shared_ptr<Texture> InstantiateTexture(const TextureInfo& params) = 0;
virtual std::shared_ptr<Texture> InstantiateTexture(const TextureInfo& params, const void* initialData, bool buildMipmaps, unsigned int srcWidth = 0, unsigned int srcHeight = 0) = 0;
virtual std::shared_ptr<TextureSampler> InstantiateTextureSampler(const TextureSamplerInfo& params) = 0; virtual std::shared_ptr<TextureSampler> InstantiateTextureSampler(const TextureSamplerInfo& params) = 0;
virtual bool IsTextureFormatSupported(PixelFormat format, TextureUsage usage) const = 0; virtual bool IsTextureFormatSupported(PixelFormat format, TextureUsage usage) const = 0;

View File

@ -25,8 +25,8 @@ namespace Nz
{ {
PixelFormat pixelFormat; PixelFormat pixelFormat;
ImageType type; ImageType type;
TextureUsageFlags usageFlags = TextureUsage::ShaderSampling | TextureUsage::TransferDestination; TextureUsageFlags usageFlags = TextureUsage::ShaderSampling | TextureUsage::TransferDestination | TextureUsage::TransferSource;
UInt8 levelCount = 1; UInt8 levelCount = 0xFF;
unsigned int layerCount = 1; unsigned int layerCount = 1;
unsigned int depth = 1; unsigned int depth = 1;
unsigned int height; unsigned int height;
@ -46,7 +46,8 @@ namespace Nz
struct NAZARA_RENDERER_API TextureParams : ImageParams struct NAZARA_RENDERER_API TextureParams : ImageParams
{ {
std::shared_ptr<RenderDevice> renderDevice; std::shared_ptr<RenderDevice> renderDevice;
TextureUsageFlags usageFlags = TextureUsage::ShaderSampling | TextureUsage::TransferDestination; TextureUsageFlags usageFlags = TextureUsage::ShaderSampling | TextureUsage::TransferDestination | TextureUsage::TransferSource;
bool buildMipmaps = true;
bool IsValid() const; bool IsValid() const;
void Merge(const TextureParams& params); void Merge(const TextureParams& params);

View File

@ -62,8 +62,9 @@ namespace Nz
Vertex, Vertex,
Storage, Storage,
Uniform, Uniform,
Upload,
Max = Uniform Max = Upload
}; };
enum class BufferUsage enum class BufferUsage

View File

@ -110,9 +110,11 @@ namespace Nz
Image& operator=(const Image& image); Image& operator=(const Image& image);
inline Image& operator=(Image&& image) noexcept; 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 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(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 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 // Load
static std::shared_ptr<Image> LoadFromFile(const std::filesystem::path& filePath, const ImageParams& params = ImageParams()); static std::shared_ptr<Image> LoadFromFile(const std::filesystem::path& filePath, const ImageParams& params = ImageParams());

View File

@ -18,6 +18,70 @@ namespace Nz
return *this; 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 <Nazara/Utility/DebugOff.hpp> #include <Nazara/Utility/DebugOff.hpp>

View File

@ -100,10 +100,11 @@ namespace Nz
{ {
switch (bufferType) 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::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::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)); NazaraError("Unhandled BufferType 0x" + NumberToString(UnderlyingCast(bufferType), 16));

View File

@ -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 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 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 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; void CopyTexture(const Texture& fromTexture, const Boxui& fromBox, TextureLayout fromLayout, const Texture& toTexture, const Vector3ui& toPos, TextureLayout toLayout) override;

View File

@ -37,6 +37,7 @@ namespace Nz
std::shared_ptr<ShaderModule> InstantiateShaderModule(nzsl::ShaderStageTypeFlags stages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const nzsl::ShaderWriter::States& states) override; std::shared_ptr<ShaderModule> InstantiateShaderModule(nzsl::ShaderStageTypeFlags stages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const nzsl::ShaderWriter::States& states) override;
std::shared_ptr<Swapchain> InstantiateSwapchain(WindowHandle windowHandle, const Vector2ui& windowSize, const SwapchainParameters& parameters) override; std::shared_ptr<Swapchain> InstantiateSwapchain(WindowHandle windowHandle, const Vector2ui& windowSize, const SwapchainParameters& parameters) override;
std::shared_ptr<Texture> InstantiateTexture(const TextureInfo& params) override; std::shared_ptr<Texture> InstantiateTexture(const TextureInfo& params) override;
std::shared_ptr<Texture> InstantiateTexture(const TextureInfo& params, const void* initialData, bool buildMipmaps, unsigned int srcWidth = 0, unsigned int srcHeight = 0) override;
std::shared_ptr<TextureSampler> InstantiateTextureSampler(const TextureSamplerInfo& params) override; std::shared_ptr<TextureSampler> InstantiateTextureSampler(const TextureSamplerInfo& params) override;
bool IsTextureFormatSupported(PixelFormat format, TextureUsage usage) const override; bool IsTextureFormatSupported(PixelFormat format, TextureUsage usage) const override;

View File

@ -16,15 +16,29 @@
namespace Nz namespace Nz
{ {
class VulkanBuffer;
class VulkanDevice;
namespace Vk
{
class CommandBuffer;
}
class NAZARA_VULKANRENDERER_API VulkanTexture final : public Texture class NAZARA_VULKANRENDERER_API VulkanTexture final : public Texture
{ {
public: 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<VulkanTexture> parentTexture, const TextureViewInfo& viewInfo); VulkanTexture(std::shared_ptr<VulkanTexture> parentTexture, const TextureViewInfo& viewInfo);
VulkanTexture(const VulkanTexture&) = delete; VulkanTexture(const VulkanTexture&) = delete;
VulkanTexture(VulkanTexture&&) = delete; VulkanTexture(VulkanTexture&&) = delete;
~VulkanTexture(); ~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; bool Copy(const Texture& source, const Boxui& srcBox, const Vector3ui& dstPos) override;
std::shared_ptr<Texture> CreateView(const TextureViewInfo& viewInfo) override; std::shared_ptr<Texture> CreateView(const TextureViewInfo& viewInfo) override;
@ -40,6 +54,7 @@ namespace Nz
using Texture::Update; using Texture::Update;
bool Update(const void* ptr, const Boxui& box, unsigned int srcWidth, unsigned int srcHeight, UInt8 level) override; 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<VulkanBuffer>& uploadBuffer, const void* ptr, const Boxui& box, unsigned int srcWidth, unsigned int srcHeight, UInt8 level);
void UpdateDebugName(std::string_view name) override; void UpdateDebugName(std::string_view name) override;
@ -51,12 +66,13 @@ namespace Nz
std::optional<TextureViewInfo> m_viewInfo; std::optional<TextureViewInfo> m_viewInfo;
std::shared_ptr<VulkanTexture> m_parentTexture; std::shared_ptr<VulkanTexture> m_parentTexture;
VulkanDevice& m_device;
VkImage m_image; VkImage m_image;
VkImageSubresourceRange m_imageRange; VkImageSubresourceRange m_subresourceRange;
VmaAllocation m_allocation; VmaAllocation m_allocation;
Vk::Device& m_device;
Vk::ImageView m_imageView; Vk::ImageView m_imageView;
TextureInfo m_textureInfo; TextureInfo m_textureInfo;
TextureInfo m_textureViewInfo;
}; };
} }

View File

@ -2,13 +2,53 @@
// This file is part of the "Nazara Engine - Vulkan renderer" // This file is part of the "Nazara Engine - Vulkan renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp // For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Core/Error.hpp>
#include <Nazara/VulkanRenderer/Debug.hpp> #include <Nazara/VulkanRenderer/Debug.hpp>
namespace Nz 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 inline PixelFormat VulkanTexture::GetFormat() const
{ {
return m_textureInfo.pixelFormat; return m_textureViewInfo.pixelFormat;
} }
inline VkImage VulkanTexture::GetImage() const inline VkImage VulkanTexture::GetImage() const
@ -23,7 +63,7 @@ namespace Nz
inline UInt8 VulkanTexture::GetLevelCount() const inline UInt8 VulkanTexture::GetLevelCount() const
{ {
return m_textureInfo.levelCount; return m_textureViewInfo.levelCount;
} }
inline VulkanTexture* VulkanTexture::GetParentTexture() const inline VulkanTexture* VulkanTexture::GetParentTexture() const
@ -33,22 +73,22 @@ namespace Nz
inline Vector3ui VulkanTexture::GetSize(UInt8 level) const 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 inline const VkImageSubresourceRange& VulkanTexture::GetSubresourceRange() const
{ {
return m_imageRange; return m_subresourceRange;
} }
inline const TextureInfo& VulkanTexture::GetTextureInfo() const inline const TextureInfo& VulkanTexture::GetTextureInfo() const
{ {
return m_textureInfo; return m_textureViewInfo;
} }
inline ImageType VulkanTexture::GetType() const inline ImageType VulkanTexture::GetType() const
{ {
return m_textureInfo.type; return m_textureViewInfo.type;
} }
} }

View File

@ -168,6 +168,7 @@ namespace Nz
textureCreationParams.type = textureData.type; textureCreationParams.type = textureData.type;
textureCreationParams.usageFlags = textureData.usage; textureCreationParams.usageFlags = textureData.usage;
textureCreationParams.pixelFormat = textureData.format; textureCreationParams.pixelFormat = textureData.format;
textureCreationParams.levelCount = 1;
if (textureCreationParams.type == ImageType::Cubemap) if (textureCreationParams.type == ImageType::Cubemap)
textureCreationParams.layerCount = 6; textureCreationParams.layerCount = 6;

View File

@ -361,8 +361,7 @@ namespace Nz
texInfo.layerCount = (texInfo.type == ImageType::Cubemap) ? 6 : 1; texInfo.layerCount = (texInfo.type == ImageType::Cubemap) ? 6 : 1;
m_defaultTextures.depthTextures[i] = m_renderDevice->InstantiateTexture(texInfo); m_defaultTextures.depthTextures[i] = m_renderDevice->InstantiateTexture(texInfo, whitePixels.data(), false);
m_defaultTextures.depthTextures[i]->Update(whitePixels.data());
} }
} }
@ -379,8 +378,7 @@ namespace Nz
texInfo.type = static_cast<ImageType>(i); texInfo.type = static_cast<ImageType>(i);
texInfo.layerCount = (texInfo.type == ImageType::Cubemap) ? 6 : 1; texInfo.layerCount = (texInfo.type == ImageType::Cubemap) ? 6 : 1;
m_defaultTextures.whiteTextures[i] = m_renderDevice->InstantiateTexture(texInfo); m_defaultTextures.whiteTextures[i] = m_renderDevice->InstantiateTexture(texInfo, whitePixels.data(), false);
m_defaultTextures.whiteTextures[i]->Update(whitePixels.data());
} }
} }
} }

View File

@ -20,6 +20,7 @@ namespace Nz
{ {
case BufferType::Index: case BufferType::Index:
case BufferType::Vertex: case BufferType::Vertex:
case BufferType::Upload:
break; // TODO break; // TODO
case BufferType::Storage: case BufferType::Storage:

View File

@ -8,6 +8,7 @@
#include <Nazara/OpenGLRenderer/OpenGLFboFramebuffer.hpp> #include <Nazara/OpenGLRenderer/OpenGLFboFramebuffer.hpp>
#include <Nazara/OpenGLRenderer/OpenGLRenderPass.hpp> #include <Nazara/OpenGLRenderer/OpenGLRenderPass.hpp>
#include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp> #include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp>
#include <Nazara/OpenGLRenderer/OpenGLTexture.hpp>
#include <Nazara/OpenGLRenderer/OpenGLVaoCache.hpp> #include <Nazara/OpenGLRenderer/OpenGLVaoCache.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Context.hpp> #include <Nazara/OpenGLRenderer/Wrapper/Context.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/VertexArray.hpp> #include <Nazara/OpenGLRenderer/Wrapper/VertexArray.hpp>
@ -145,6 +146,11 @@ namespace Nz
context->BlitTexture(*command.source, *command.target, command.sourceBox, command.targetBox, command.filter); 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) inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const CopyBufferCommand& command)
{ {
context->BindBuffer(GL::BufferTarget::CopyRead, command.source); 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); 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) if (context->glPopDebugGroup)
context->glPopDebugGroup(); context->glPopDebugGroup();

View File

@ -93,6 +93,13 @@ namespace Nz
m_commandBuffer.BlitTexture(sourceTexture, fromBox, targetTexture, toBox, filter); m_commandBuffer.BlitTexture(sourceTexture, fromBox, targetTexture, toBox, filter);
} }
void OpenGLCommandBufferBuilder::BuildMipmaps(Texture& texture, UInt8 baseLevel, UInt8 maxLevel)
{
OpenGLTexture& glTexture = static_cast<OpenGLTexture&>(texture);
glTexture.GenerateMipmaps(baseLevel, maxLevel);
}
void OpenGLCommandBufferBuilder::CopyBuffer(const RenderBufferView& source, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset, UInt64 targetOffset) void OpenGLCommandBufferBuilder::CopyBuffer(const RenderBufferView& source, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset, UInt64 targetOffset)
{ {
OpenGLBuffer& sourceBuffer = *static_cast<OpenGLBuffer*>(source.GetBuffer()); OpenGLBuffer& sourceBuffer = *static_cast<OpenGLBuffer*>(source.GetBuffer());

View File

@ -221,6 +221,11 @@ namespace Nz
return std::make_shared<OpenGLTexture>(*this, params); return std::make_shared<OpenGLTexture>(*this, params);
} }
std::shared_ptr<Texture> OpenGLDevice::InstantiateTexture(const TextureInfo& params, const void* initialData, bool buildMipmaps, unsigned int srcWidth, unsigned int srcHeight)
{
return std::make_shared<OpenGLTexture>(*this, params, initialData, buildMipmaps, srcWidth, srcHeight);
}
std::shared_ptr<TextureSampler> OpenGLDevice::InstantiateTextureSampler(const TextureSamplerInfo& params) std::shared_ptr<TextureSampler> OpenGLDevice::InstantiateTextureSampler(const TextureSamplerInfo& params)
{ {
return std::make_shared<OpenGLTextureSampler>(*this, params); return std::make_shared<OpenGLTextureSampler>(*this, params);

View File

@ -13,6 +13,8 @@ namespace Nz
OpenGLTexture::OpenGLTexture(OpenGLDevice& device, const TextureInfo& textureInfo) : OpenGLTexture::OpenGLTexture(OpenGLDevice& device, const TextureInfo& textureInfo) :
m_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)) if (!m_texture.Create(device))
throw std::runtime_error("failed to create texture object"); throw std::runtime_error("failed to create texture object");
@ -71,6 +73,16 @@ namespace Nz
#endif #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<OpenGLTexture> parentTexture, const TextureViewInfo& viewInfo) : OpenGLTexture::OpenGLTexture(std::shared_ptr<OpenGLTexture> parentTexture, const TextureViewInfo& viewInfo) :
m_parentTexture(std::move(parentTexture)) m_parentTexture(std::move(parentTexture))
{ {

View File

@ -46,8 +46,7 @@ namespace Nz
{ {
NazaraAssert(params.IsValid(), "Invalid TextureParams"); NazaraAssert(params.IsValid(), "Invalid TextureParams");
Nz::TextureInfo texParams; TextureInfo texParams;
texParams.height = image.GetHeight();
texParams.pixelFormat = image.GetFormat(); texParams.pixelFormat = image.GetFormat();
texParams.type = image.GetType(); texParams.type = image.GetType();
texParams.width = image.GetWidth(); texParams.width = image.GetWidth();
@ -56,32 +55,38 @@ namespace Nz
switch (image.GetType()) switch (image.GetType())
{ {
case ImageType::E1D: case ImageType::E1D:
texParams.height = 1;
break;
case ImageType::E2D: case ImageType::E2D:
texParams.height = image.GetHeight();
break;
case ImageType::E3D: case ImageType::E3D:
texParams.height = image.GetHeight();
texParams.depth = image.GetDepth();
break; break;
case ImageType::E1D_Array: case ImageType::E1D_Array:
texParams.height = 1;
texParams.layerCount = image.GetHeight(); texParams.layerCount = image.GetHeight();
break; break;
case ImageType::E2D_Array: case ImageType::E2D_Array:
texParams.height = image.GetHeight();
texParams.layerCount = image.GetDepth(); texParams.layerCount = image.GetDepth();
break; break;
case ImageType::Cubemap: case ImageType::Cubemap:
texParams.height = image.GetHeight();
texParams.layerCount = 6; texParams.layerCount = 6;
break; break;
} }
std::shared_ptr<Texture> texture = params.renderDevice->InstantiateTexture(texParams); std::shared_ptr<Texture> texture = params.renderDevice->InstantiateTexture(texParams, image.GetConstPixels(), params.buildMipmaps);
if (!texture->Update(image.GetConstPixels()))
{
NazaraError("failed to update texture");
return {};
}
texture->SetFilePath(image.GetFilePath()); 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); texture->UpdateDebugName(debugName);
return texture; return texture;

View File

@ -1351,7 +1351,7 @@ namespace Nz
UInt8 Image::GetMaxLevel(unsigned int width, unsigned int height, unsigned int depth) UInt8 Image::GetMaxLevel(unsigned int width, unsigned int height, unsigned int depth)
{ {
// Le niveau maximal est le niveau requis pour la plus grande taille // Le niveau maximal est le niveau requis pour la plus grande taille
return SafeCast<UInt8>(std::max(IntegralLog2(std::max({width, height, depth})), 1U)); return SafeCast<UInt8>(std::max(IntegralLog2(std::max({ width, height, depth })), 1U));
} }
UInt8 Image::GetMaxLevel(ImageType type, unsigned int width, unsigned int height, unsigned int depth) UInt8 Image::GetMaxLevel(ImageType type, unsigned int width, unsigned int height, unsigned int depth)

View File

@ -28,7 +28,9 @@ namespace Nz
//TODO: Update for VMA 3.0 //TODO: Update for VMA 3.0
VmaAllocationCreateInfo allocInfo = {}; 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) if (usage & BufferUsage::DirectMapping)
allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;

View File

@ -141,15 +141,14 @@ namespace Nz
const VulkanTexture& vkFromTexture = static_cast<const VulkanTexture&>(fromTexture); const VulkanTexture& vkFromTexture = static_cast<const VulkanTexture&>(fromTexture);
const VulkanTexture& vkToTexture = static_cast<const VulkanTexture&>(toTexture); const VulkanTexture& vkToTexture = static_cast<const VulkanTexture&>(toTexture);
VkImageSubresourceLayers todo = { unsigned int fromBaseLayer, fromLayerCount;
VK_IMAGE_ASPECT_COLOR_BIT, Image::RegionToArray(vkFromTexture.GetType(), fromBox, fromBaseLayer, fromLayerCount);
1,
0, unsigned int toBaseLayer, toLayerCount;
1 Image::RegionToArray(vkToTexture.GetType(), toBox, toBaseLayer, toLayerCount);
};
VkImageBlit region = { VkImageBlit region = {
todo, vkFromTexture.BuildSubresourceLayers(0, fromBaseLayer, fromLayerCount),
{ {
{ {
SafeCast<Int32>(fromBox.x), SafeCast<Int32>(fromBox.x),
@ -162,7 +161,7 @@ namespace Nz
SafeCast<Int32>(fromBox.z + fromBox.depth) SafeCast<Int32>(fromBox.z + fromBox.depth)
} }
}, },
todo, vkToTexture.BuildSubresourceLayers(0, toBaseLayer, toLayerCount),
{ {
{ {
SafeCast<Int32>(toBox.x), SafeCast<Int32>(toBox.x),
@ -180,6 +179,13 @@ namespace Nz
m_commandBuffer.BlitImage(vkFromTexture.GetImage(), ToVulkan(fromLayout), vkToTexture.GetImage(), ToVulkan(toLayout), region, ToVulkan(filter)); 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<VulkanTexture&>(texture);
// TODO
}
void VulkanCommandBufferBuilder::CopyBuffer(const RenderBufferView& source, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset, UInt64 targetOffset) void VulkanCommandBufferBuilder::CopyBuffer(const RenderBufferView& source, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset, UInt64 targetOffset)
{ {
VulkanBuffer& sourceBuffer = *static_cast<VulkanBuffer*>(source.GetBuffer()); VulkanBuffer& sourceBuffer = *static_cast<VulkanBuffer*>(source.GetBuffer());
@ -201,21 +207,20 @@ namespace Nz
const VulkanTexture& vkFromTexture = static_cast<const VulkanTexture&>(fromTexture); const VulkanTexture& vkFromTexture = static_cast<const VulkanTexture&>(fromTexture);
const VulkanTexture& vkToTexture = static_cast<const VulkanTexture&>(toTexture); const VulkanTexture& vkToTexture = static_cast<const VulkanTexture&>(toTexture);
VkImageSubresourceLayers todo = { unsigned int fromBaseLayer, fromLayerCount;
VK_IMAGE_ASPECT_COLOR_BIT, Image::RegionToArray(vkFromTexture.GetType(), fromBox, fromBaseLayer, fromLayerCount);
1,
0, unsigned int toBaseLayer, toLayerCount;
1 Image::RegionToArray(vkToTexture.GetType(), Boxui(toPos.x, toPos.y, toPos.z, fromBox.width, fromBox.height, fromBox.depth), toBaseLayer, toLayerCount);
};
VkImageCopy region = { VkImageCopy region = {
todo, vkFromTexture.BuildSubresourceLayers(0, fromBaseLayer, fromLayerCount),
{ {
SafeCast<Int32>(fromBox.x), SafeCast<Int32>(fromBox.x),
SafeCast<Int32>(fromBox.y), SafeCast<Int32>(fromBox.y),
SafeCast<Int32>(fromBox.z) SafeCast<Int32>(fromBox.z)
}, },
todo, vkToTexture.BuildSubresourceLayers(0, toBaseLayer, toLayerCount),
{ {
SafeCast<Int32>(toPos.x), SafeCast<Int32>(toPos.x),
SafeCast<Int32>(toPos.y), SafeCast<Int32>(toPos.y),

View File

@ -96,6 +96,11 @@ namespace Nz
return std::make_shared<VulkanTexture>(*this, params); return std::make_shared<VulkanTexture>(*this, params);
} }
std::shared_ptr<Texture> VulkanDevice::InstantiateTexture(const TextureInfo& params, const void* initialData, bool buildMipmaps, unsigned int srcWidth, unsigned int srcHeight)
{
return std::make_shared<VulkanTexture>(*this, params, initialData, buildMipmaps, srcWidth, srcHeight);
}
std::shared_ptr<TextureSampler> VulkanDevice::InstantiateTextureSampler(const TextureSamplerInfo& params) std::shared_ptr<TextureSampler> VulkanDevice::InstantiateTextureSampler(const TextureSamplerInfo& params)
{ {
return std::make_shared<VulkanTextureSampler>(*this, params); return std::make_shared<VulkanTextureSampler>(*this, params);

View File

@ -4,6 +4,8 @@
#include <Nazara/VulkanRenderer/VulkanTexture.hpp> #include <Nazara/VulkanRenderer/VulkanTexture.hpp>
#include <Nazara/Utility/PixelFormat.hpp> #include <Nazara/Utility/PixelFormat.hpp>
#include <Nazara/VulkanRenderer/VulkanBuffer.hpp>
#include <Nazara/VulkanRenderer/VulkanDevice.hpp>
#include <Nazara/VulkanRenderer/Wrapper/CommandBuffer.hpp> #include <Nazara/VulkanRenderer/Wrapper/CommandBuffer.hpp>
#include <Nazara/VulkanRenderer/Wrapper/QueueHandle.hpp> #include <Nazara/VulkanRenderer/Wrapper/QueueHandle.hpp>
#include <NazaraUtils/CallOnExit.hpp> #include <NazaraUtils/CallOnExit.hpp>
@ -13,15 +15,18 @@
namespace Nz 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_image(VK_NULL_HANDLE),
m_allocation(nullptr), 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 = {}; VkImageViewCreateInfo createInfoView = {};
createInfoView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfoView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
InitViewForFormat(textureInfo.pixelFormat, createInfoView); InitViewForFormat(m_textureInfo.pixelFormat, createInfoView);
VkImageCreateInfo createInfo = {}; VkImageCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
@ -29,65 +34,65 @@ namespace Nz
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
createInfo.samples = VK_SAMPLE_COUNT_1_BIT; createInfo.samples = VK_SAMPLE_COUNT_1_BIT;
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL; 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: case ImageType::E1D:
NazaraAssert(textureInfo.width > 0, "Width must be over zero"); NazaraAssert(m_textureInfo.width > 0, "Width must be over zero");
NazaraAssert(textureInfo.height == 1, "Height must be one"); NazaraAssert(m_textureInfo.height == 1, "Height must be one");
NazaraAssert(textureInfo.depth == 1, "Depth must be one"); NazaraAssert(m_textureInfo.depth == 1, "Depth must be one");
NazaraAssert(textureInfo.layerCount == 1, "Array count must be one"); NazaraAssert(m_textureInfo.layerCount == 1, "Array count must be one");
createInfo.imageType = VK_IMAGE_TYPE_1D; createInfo.imageType = VK_IMAGE_TYPE_1D;
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D;
break; break;
case ImageType::E1D_Array: case ImageType::E1D_Array:
NazaraAssert(textureInfo.width > 0, "Width must be over zero"); NazaraAssert(m_textureInfo.width > 0, "Width must be over zero");
NazaraAssert(textureInfo.height == 1, "Height must be one"); NazaraAssert(m_textureInfo.height == 1, "Height must be one");
NazaraAssert(textureInfo.depth == 1, "Depth must be one"); NazaraAssert(m_textureInfo.depth == 1, "Depth must be one");
NazaraAssert(textureInfo.layerCount > 0, "Array count must be over zero"); NazaraAssert(m_textureInfo.layerCount > 0, "Array count must be over zero");
createInfo.imageType = VK_IMAGE_TYPE_1D; createInfo.imageType = VK_IMAGE_TYPE_1D;
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
break; break;
case ImageType::E2D: case ImageType::E2D:
NazaraAssert(textureInfo.width > 0, "Width must be over zero"); NazaraAssert(m_textureInfo.width > 0, "Width must be over zero");
NazaraAssert(textureInfo.height > 0, "Height must be over zero"); NazaraAssert(m_textureInfo.height > 0, "Height must be over zero");
NazaraAssert(textureInfo.depth == 1, "Depth must be one"); NazaraAssert(m_textureInfo.depth == 1, "Depth must be one");
NazaraAssert(textureInfo.layerCount == 1, "Array count must be one"); NazaraAssert(m_textureInfo.layerCount == 1, "Array count must be one");
createInfo.imageType = VK_IMAGE_TYPE_2D; createInfo.imageType = VK_IMAGE_TYPE_2D;
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D;
break; break;
case ImageType::E2D_Array: case ImageType::E2D_Array:
NazaraAssert(textureInfo.width > 0, "Width must be over zero"); NazaraAssert(m_textureInfo.width > 0, "Width must be over zero");
NazaraAssert(textureInfo.height > 0, "Height must be over zero"); NazaraAssert(m_textureInfo.height > 0, "Height must be over zero");
NazaraAssert(textureInfo.depth == 1, "Depth must be one"); NazaraAssert(m_textureInfo.depth == 1, "Depth must be one");
NazaraAssert(textureInfo.layerCount > 0, "Array count must be over zero"); NazaraAssert(m_textureInfo.layerCount > 0, "Array count must be over zero");
createInfo.imageType = VK_IMAGE_TYPE_2D; createInfo.imageType = VK_IMAGE_TYPE_2D;
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
break; break;
case ImageType::E3D: case ImageType::E3D:
NazaraAssert(textureInfo.width > 0, "Width must be over zero"); NazaraAssert(m_textureInfo.width > 0, "Width must be over zero");
NazaraAssert(textureInfo.height > 0, "Height must be over zero"); NazaraAssert(m_textureInfo.height > 0, "Height must be over zero");
NazaraAssert(textureInfo.depth > 0, "Depth must be over zero"); NazaraAssert(m_textureInfo.depth > 0, "Depth must be over zero");
NazaraAssert(textureInfo.layerCount == 1, "Array count must be one"); NazaraAssert(m_textureInfo.layerCount == 1, "Array count must be one");
createInfo.imageType = VK_IMAGE_TYPE_3D; createInfo.imageType = VK_IMAGE_TYPE_3D;
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_3D; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_3D;
break; break;
case ImageType::Cubemap: case ImageType::Cubemap:
NazaraAssert(textureInfo.width > 0, "Width must be over zero"); NazaraAssert(m_textureInfo.width > 0, "Width must be over zero");
NazaraAssert(textureInfo.height > 0, "Height must be over zero"); NazaraAssert(m_textureInfo.height > 0, "Height must be over zero");
NazaraAssert(textureInfo.depth == 1, "Depth must be one"); NazaraAssert(m_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.layerCount > 0 && m_textureInfo.layerCount % 6 == 0, "Array count must be a multiple of 6");
createInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; createInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
createInfo.imageType = VK_IMAGE_TYPE_2D; createInfo.imageType = VK_IMAGE_TYPE_2D;
@ -95,11 +100,11 @@ namespace Nz
break; break;
} }
createInfo.extent.width = textureInfo.width; createInfo.extent.width = m_textureInfo.width;
createInfo.extent.height = textureInfo.height; createInfo.extent.height = m_textureInfo.height;
createInfo.extent.depth = textureInfo.depth; createInfo.extent.depth = m_textureInfo.depth;
createInfo.arrayLayers = textureInfo.layerCount; createInfo.arrayLayers = m_textureInfo.layerCount;
createInfo.mipLevels = textureInfo.levelCount; createInfo.mipLevels = m_textureInfo.levelCount;
VmaAllocationCreateInfo allocInfo = {}; VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; allocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
@ -111,8 +116,8 @@ namespace Nz
CallOnExit releaseImage([&]{ vmaDestroyImage(m_device.GetMemoryAllocator(), m_image, m_allocation); }); CallOnExit releaseImage([&]{ vmaDestroyImage(m_device.GetMemoryAllocator(), m_image, m_allocation); });
// Create default view (viewing the whole texture) // Create default view (viewing the whole texture)
m_imageRange = { m_subresourceRange = {
ToVulkan(PixelFormatInfo::GetContent(textureInfo.pixelFormat)), ToVulkan(PixelFormatInfo::GetContent(m_textureInfo.pixelFormat)),
0, //< baseMipLevel 0, //< baseMipLevel
createInfo.mipLevels, //< levelCount createInfo.mipLevels, //< levelCount
0, //< baseArrayLayer 0, //< baseArrayLayer
@ -120,7 +125,7 @@ namespace Nz
}; };
createInfoView.image = m_image; createInfoView.image = m_image;
createInfoView.subresourceRange = m_imageRange; createInfoView.subresourceRange = m_subresourceRange;
if (!m_imageView.Create(m_device, createInfoView)) if (!m_imageView.Create(m_device, createInfoView))
throw std::runtime_error("Failed to create default image view: " + TranslateVulkanError(m_imageView.GetLastErrorCode())); throw std::runtime_error("Failed to create default image view: " + TranslateVulkanError(m_imageView.GetLastErrorCode()));
@ -128,19 +133,91 @@ namespace Nz
releaseImage.Reset(); 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<VulkanBuffer> 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<Int32>(m_textureInfo.width), SafeCast<Int32>(m_textureInfo.height), SafeCast<Int32>(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<VulkanTexture> parentTexture, const TextureViewInfo& viewInfo) : VulkanTexture::VulkanTexture(std::shared_ptr<VulkanTexture> parentTexture, const TextureViewInfo& viewInfo) :
m_parentTexture(std::move(parentTexture)), m_parentTexture(std::move(parentTexture)),
m_device(m_parentTexture->m_device),
m_image(m_parentTexture->m_image), m_image(m_parentTexture->m_image),
m_allocation(nullptr), 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.layerCount <= m_parentTexture->m_textureViewInfo.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.levelCount <= m_parentTexture->m_textureViewInfo.levelCount - viewInfo.baseMipLevel, "level count exceeds number of levels");
m_viewInfo = viewInfo; m_viewInfo = viewInfo;
m_imageRange = { m_subresourceRange = {
ToVulkan(PixelFormatInfo::GetContent(viewInfo.reinterpretFormat)), ToVulkan(PixelFormatInfo::GetContent(viewInfo.reinterpretFormat)),
viewInfo.baseMipLevel, viewInfo.baseMipLevel,
viewInfo.levelCount, viewInfo.levelCount,
@ -151,60 +228,60 @@ namespace Nz
VkImageViewCreateInfo createInfoView = {}; VkImageViewCreateInfo createInfoView = {};
createInfoView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfoView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfoView.image = m_image; createInfoView.image = m_image;
createInfoView.subresourceRange = m_imageRange; createInfoView.subresourceRange = m_subresourceRange;
switch (m_textureInfo.type) switch (m_textureViewInfo.type)
{ {
case ImageType::E1D: case ImageType::E1D:
NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); NazaraAssert(m_textureViewInfo.width > 0, "Width must be over zero");
NazaraAssert(m_textureInfo.height == 1, "Height must be one"); NazaraAssert(m_textureViewInfo.height == 1, "Height must be one");
NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); NazaraAssert(m_textureViewInfo.depth == 1, "Depth must be one");
NazaraAssert(m_textureInfo.layerCount == 1, "Array count must be one"); NazaraAssert(m_textureViewInfo.layerCount == 1, "Array count must be one");
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D;
break; break;
case ImageType::E1D_Array: case ImageType::E1D_Array:
NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); NazaraAssert(m_textureViewInfo.width > 0, "Width must be over zero");
NazaraAssert(m_textureInfo.height == 1, "Height must be one"); NazaraAssert(m_textureViewInfo.height == 1, "Height must be one");
NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); NazaraAssert(m_textureViewInfo.depth == 1, "Depth must be one");
NazaraAssert(m_textureInfo.layerCount > 0, "Array count must be over zero"); NazaraAssert(m_textureViewInfo.layerCount > 0, "Array count must be over zero");
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
break; break;
case ImageType::E2D: case ImageType::E2D:
NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); NazaraAssert(m_textureViewInfo.width > 0, "Width must be over zero");
NazaraAssert(m_textureInfo.height > 0, "Height must be over zero"); NazaraAssert(m_textureViewInfo.height > 0, "Height must be over zero");
NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); NazaraAssert(m_textureViewInfo.depth == 1, "Depth must be one");
NazaraAssert(m_textureInfo.layerCount == 1, "Array count must be one"); NazaraAssert(m_textureViewInfo.layerCount == 1, "Array count must be one");
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D;
break; break;
case ImageType::E2D_Array: case ImageType::E2D_Array:
NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); NazaraAssert(m_textureViewInfo.width > 0, "Width must be over zero");
NazaraAssert(m_textureInfo.height > 0, "Height must be over zero"); NazaraAssert(m_textureViewInfo.height > 0, "Height must be over zero");
NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); NazaraAssert(m_textureViewInfo.depth == 1, "Depth must be one");
NazaraAssert(m_textureInfo.layerCount > 0, "Array count must be over zero"); NazaraAssert(m_textureViewInfo.layerCount > 0, "Array count must be over zero");
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
break; break;
case ImageType::E3D: case ImageType::E3D:
NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); NazaraAssert(m_textureViewInfo.width > 0, "Width must be over zero");
NazaraAssert(m_textureInfo.height > 0, "Height must be over zero"); NazaraAssert(m_textureViewInfo.height > 0, "Height must be over zero");
NazaraAssert(m_textureInfo.depth > 0, "Depth must be over zero"); NazaraAssert(m_textureViewInfo.depth > 0, "Depth must be over zero");
NazaraAssert(m_textureInfo.layerCount == 1, "Array count must be one"); NazaraAssert(m_textureViewInfo.layerCount == 1, "Array count must be one");
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_3D; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_3D;
break; break;
case ImageType::Cubemap: case ImageType::Cubemap:
NazaraAssert(m_textureInfo.width > 0, "Width must be over zero"); NazaraAssert(m_textureViewInfo.width > 0, "Width must be over zero");
NazaraAssert(m_textureInfo.height > 0, "Height must be over zero"); NazaraAssert(m_textureViewInfo.height > 0, "Height must be over zero");
NazaraAssert(m_textureInfo.depth == 1, "Depth must be one"); NazaraAssert(m_textureViewInfo.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.layerCount > 0 && m_textureViewInfo.layerCount % 6 == 0, "Array count must be a multiple of 6");
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_CUBE; createInfoView.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
break; break;
@ -296,34 +373,40 @@ namespace Nz
bool VulkanTexture::Update(const void* ptr, const Boxui& box, unsigned int srcWidth, unsigned int srcHeight, UInt8 level) 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); Vk::AutoCommandBuffer copyCommandBuffer = m_device.AllocateCommandBuffer(QueueType::Graphics);
if (m_textureInfo.type == ImageType::Cubemap) if (!copyCommandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT))
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));
return false; return false;
}
CallOnExit freeStaging([&] { unsigned int baseLayer, layerCount;
vmaDestroyBuffer(m_device.GetMemoryAllocator(), stagingBuffer, stagingAllocation); 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<VulkanBuffer> 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<VulkanBuffer>& 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<VulkanBuffer>(m_device, BufferType::Upload, memorySize, BufferUsage::DirectMapping);
void* mappedUploadBuffer = uploadBuffer->Map(0, memorySize);
if (srcWidth == 0) if (srcWidth == 0)
srcWidth = box.width; srcWidth = box.width;
@ -332,13 +415,13 @@ namespace Nz
srcHeight = box.height; srcHeight = box.height;
if (srcWidth == box.width && srcHeight == box.height) if (srcWidth == box.width && srcHeight == box.height)
std::memcpy(allocationInfo.pMappedData, ptr, textureSize); std::memcpy(mappedUploadBuffer, ptr, memorySize);
else else
{ {
unsigned int dstWidth = box.width; unsigned int dstWidth = box.width;
unsigned int dstHeight = box.height; 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 lineStride = box.width * bpp;
unsigned int dstLineStride = dstWidth * bpp; unsigned int dstLineStride = dstWidth * bpp;
unsigned int dstFaceStride = dstLineStride * dstHeight; unsigned int dstFaceStride = dstLineStride * dstHeight;
@ -346,7 +429,7 @@ namespace Nz
unsigned int srcFaceStride = srcLineStride * srcHeight; unsigned int srcFaceStride = srcLineStride * srcHeight;
const UInt8* source = static_cast<const UInt8*>(ptr); const UInt8* source = static_cast<const UInt8*>(ptr);
UInt8* destination = static_cast<UInt8*>(allocationInfo.pMappedData); UInt8* destination = static_cast<UInt8*>(mappedUploadBuffer);
for (unsigned int i = 0; i < box.depth; ++i) for (unsigned int i = 0; i < box.depth; ++i)
{ {
UInt8* dstFacePtr = destination; UInt8* dstFacePtr = destination;
@ -364,28 +447,12 @@ namespace Nz
} }
} }
Vk::AutoCommandBuffer copyCommandBuffer = m_device.AllocateCommandBuffer(QueueType::Graphics); uploadBuffer->Unmap();
if (!copyCommandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT))
return false;
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT; unsigned int baseLayer, layerCount;
if (PixelFormatInfo::GetContent(m_textureInfo.pixelFormat) == PixelFormatContent::Depth) Boxui copyBox = Image::RegionToArray(m_textureViewInfo.type, box, baseLayer, layerCount);
aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
VkImageSubresourceLayers subresourceLayers = { //< FIXME VkImageSubresourceLayers subresourceLayers = BuildSubresourceLayers(level, baseLayer, layerCount);
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
};
VkBufferImageCopy region = { VkBufferImageCopy region = {
0, 0,
@ -393,34 +460,24 @@ namespace Nz
0, 0,
subresourceLayers, subresourceLayers,
{ // imageOffset { // imageOffset
SafeCast<Int32>(box.x), SafeCast<Int32>(box.y), SafeCast<Int32>(box.z) SafeCast<Int32>(copyBox.x), SafeCast<Int32>(copyBox.y), SafeCast<Int32>(copyBox.z)
}, },
{ // imageExtent { // 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); commandBuffer.CopyBufferToImage(uploadBuffer->GetBuffer(), m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region);
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();
return true; return true;
} }
void VulkanTexture::UpdateDebugName(std::string_view name) 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<VkImageView>(m_imageView)), name);
} }
void VulkanTexture::InitViewForFormat(PixelFormat pixelFormat, VkImageViewCreateInfo& createImageView) void VulkanTexture::InitViewForFormat(PixelFormat pixelFormat, VkImageViewCreateInfo& createImageView)

View File

@ -24,6 +24,7 @@ namespace Nz
createInfo.mipmapMode = ToVulkan(samplerInfo.mipmapMode); createInfo.mipmapMode = ToVulkan(samplerInfo.mipmapMode);
createInfo.compareEnable = samplerInfo.depthCompare; createInfo.compareEnable = samplerInfo.depthCompare;
createInfo.compareOp = ToVulkan(samplerInfo.depthComparison); createInfo.compareOp = ToVulkan(samplerInfo.depthComparison);
createInfo.maxLod = VK_LOD_CLAMP_NONE;
if (samplerInfo.anisotropyLevel > 1.f) if (samplerInfo.anisotropyLevel > 1.f)
{ {