Improve texture view support

This commit is contained in:
SirLynix 2022-12-01 18:06:20 +01:00 committed by Jérôme Leclercq
parent 42f8cdb151
commit 08ea4c87a7
17 changed files with 128 additions and 75 deletions

View File

@ -25,6 +25,7 @@ namespace Nz
class OpenGLCommandPool;
class OpenGLFramebuffer;
class OpenGLRenderPass;
class OpenGLTexture;
class NAZARA_OPENGLRENDERER_API OpenGLCommandBuffer final : public CommandBuffer
{
@ -41,11 +42,11 @@ namespace Nz
inline void BindPipeline(const OpenGLRenderPipeline* pipeline);
inline void BindShaderBinding(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 set, const OpenGLShaderBinding* binding);
inline void BindVertexBuffer(UInt32 binding, GLuint vertexBuffer, UInt64 offset = 0);
inline void BlitTexture(const GL::Texture& source, const Boxui& sourceBox, const GL::Texture& 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 CopyBuffer(GLuint source, GLuint target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0);
inline void CopyBuffer(const UploadPool::Allocation& allocation, GLuint target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0);
inline void CopyTexture(const GL::Texture& source, const Boxui& sourceBox, const GL::Texture& target, const Vector3ui& targetPoint);
inline void CopyTexture(const OpenGLTexture& source, const Boxui& sourceBox, const OpenGLTexture& target, const Vector3ui& targetPoint);
inline void Draw(UInt32 vertexCount, UInt32 instanceCount = 1, UInt32 firstVertex = 0, UInt32 firstInstance = 0);
inline void DrawIndexed(UInt32 indexCount, UInt32 instanceCount = 1, UInt32 firstIndex = 0, UInt32 firstInstance = 0);
@ -81,8 +82,8 @@ namespace Nz
struct BlitTextureData
{
const GL::Texture* source;
const GL::Texture* target;
const OpenGLTexture* source;
const OpenGLTexture* target;
Boxui sourceBox;
Boxui targetBox;
SamplerFilter filter;
@ -99,8 +100,8 @@ namespace Nz
struct CopyTextureData
{
const GL::Texture* source;
const GL::Texture* target;
const OpenGLTexture* source;
const OpenGLTexture* target;
Boxui sourceBox;
Vector3ui targetPoint;
};

View File

@ -63,7 +63,7 @@ namespace Nz
vertexBufferData.vertexBuffer = vertexBuffer;
}
inline void OpenGLCommandBuffer::BlitTexture(const GL::Texture& source, const Boxui& sourceBox, const GL::Texture& target, const Boxui& targetBox, SamplerFilter filter)
inline void OpenGLCommandBuffer::BlitTexture(const OpenGLTexture& source, const Boxui& sourceBox, const OpenGLTexture& target, const Boxui& targetBox, SamplerFilter filter)
{
BlitTextureData blitTexture = {
&source,
@ -101,7 +101,7 @@ namespace Nz
m_commands.emplace_back(std::move(copyBuffer));
}
inline void OpenGLCommandBuffer::CopyTexture(const GL::Texture& source, const Boxui& sourceBox, const GL::Texture& target, const Vector3ui& targetPoint)
inline void OpenGLCommandBuffer::CopyTexture(const OpenGLTexture& source, const Boxui& sourceBox, const OpenGLTexture& target, const Vector3ui& targetPoint)
{
CopyTextureData copyTexture = {
&source,

View File

@ -14,6 +14,7 @@
namespace Nz
{
class OpenGLCommandBuffer;
class OpenGLTexture;
class NAZARA_OPENGLRENDERER_API OpenGLCommandBufferBuilder final : public CommandBufferBuilder
{

View File

@ -33,9 +33,10 @@ namespace Nz
OpenGLTexture* GetParentTexture() const override;
Vector3ui GetSize(UInt8 level = 0) const override;
inline const GL::Texture& GetTexture() const;
inline const TextureViewInfo& GetTextureViewInfo() const;
ImageType GetType() const override;
inline bool RequireTextureViewEmulation() const;
inline bool RequiresTextureViewEmulation() const;
using Texture::Update;
bool Update(const void* ptr, const Boxui& box, unsigned int srcWidth = 0, unsigned int srcHeight = 0, UInt8 level = 0) override;

View File

@ -13,7 +13,13 @@ namespace Nz
return m_texture;
}
inline bool OpenGLTexture::RequireTextureViewEmulation() const
inline const TextureViewInfo& OpenGLTexture::GetTextureViewInfo() const
{
assert(m_viewInfo);
return *m_viewInfo;
}
inline bool OpenGLTexture::RequiresTextureViewEmulation() const
{
return m_viewInfo.has_value() && !m_texture.IsValid();
}

View File

@ -23,6 +23,7 @@
namespace Nz
{
class OpenGLDevice;
class OpenGLTexture;
}
namespace Nz::GL
@ -134,11 +135,11 @@ namespace Nz::GL
void BindUniformBuffer(UInt32 uboUnit, GLuint buffer, GLintptr offset, GLsizeiptr size) const;
void BindVertexArray(GLuint vertexArray, bool force = false) const;
bool BlitTexture(const Texture& source, const Texture& destination, const Boxui& srcBox, const Boxui& dstBox, SamplerFilter filter) const;
bool BlitTexture(const OpenGLTexture& source, const OpenGLTexture& destination, const Boxui& srcBox, const Boxui& dstBox, SamplerFilter filter) const;
bool ClearErrorStack() const;
bool CopyTexture(const Texture& source, const Texture& destination, const Boxui& srcBox, const Vector3ui& dstPos) const;
bool CopyTexture(const OpenGLTexture& source, const OpenGLTexture& destination, const Boxui& srcBox, const Vector3ui& dstPos) const;
inline bool DidLastCallSucceed() const;

View File

@ -19,6 +19,7 @@ namespace Nz
bool depthClamping = false;
bool nonSolidFaceFilling = false;
bool storageBuffers = false;
bool unrestrictedTextureViews = false;
};
struct RenderDeviceLimits

View File

@ -12,6 +12,7 @@
#include <Nazara/VulkanRenderer/Config.hpp>
#include <Nazara/VulkanRenderer/Wrapper/Image.hpp>
#include <Nazara/VulkanRenderer/Wrapper/ImageView.hpp>
#include <optional>
namespace Nz
{
@ -46,8 +47,10 @@ namespace Nz
private:
static void InitViewForFormat(PixelFormat pixelFormat, VkImageViewCreateInfo& createImageView);
std::optional<TextureViewInfo> m_viewInfo;
std::shared_ptr<VulkanTexture> m_parentTexture;
VkImage m_image;
VkImageSubresourceRange m_imageRange;
VmaAllocation m_allocation;
Vk::Device& m_device;
Vk::ImageView m_imageView;

View File

@ -110,6 +110,8 @@ namespace Nz
enabledFeatures.anisotropicFiltering = !config.forceDisableFeatures.anisotropicFiltering && renderDeviceInfo[bestRenderDeviceIndex].features.anisotropicFiltering;
enabledFeatures.depthClamping = !config.forceDisableFeatures.depthClamping && renderDeviceInfo[bestRenderDeviceIndex].features.depthClamping;
enabledFeatures.nonSolidFaceFilling = !config.forceDisableFeatures.nonSolidFaceFilling && renderDeviceInfo[bestRenderDeviceIndex].features.nonSolidFaceFilling;
enabledFeatures.storageBuffers = !config.forceDisableFeatures.storageBuffers && renderDeviceInfo[bestRenderDeviceIndex].features.storageBuffers;
enabledFeatures.unrestrictedTextureViews = !config.forceDisableFeatures.unrestrictedTextureViews && renderDeviceInfo[bestRenderDeviceIndex].features.unrestrictedTextureViews;
m_renderDevice = renderer->InstanciateRenderDevice(bestRenderDeviceIndex, enabledFeatures);
if (!m_renderDevice)

View File

@ -11,6 +11,7 @@
#include <Nazara/OpenGLRenderer/OpenGLTexture.hpp>
#include <Nazara/OpenGLRenderer/OpenGLUploadPool.hpp>
#include <Nazara/Utils/StackArray.hpp>
#include <stdexcept>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
@ -66,7 +67,7 @@ namespace Nz
const OpenGLTexture& sourceTexture = static_cast<const OpenGLTexture&>(fromTexture);
const OpenGLTexture& targetTexture = static_cast<const OpenGLTexture&>(toTexture);
m_commandBuffer.BlitTexture(sourceTexture.GetTexture(), fromBox, targetTexture.GetTexture(), toBox, filter);
m_commandBuffer.BlitTexture(sourceTexture, fromBox, targetTexture, toBox, filter);
}
void OpenGLCommandBufferBuilder::CopyBuffer(const RenderBufferView& source, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset, UInt64 targetOffset)
@ -89,7 +90,7 @@ namespace Nz
const OpenGLTexture& sourceTexture = static_cast<const OpenGLTexture&>(fromTexture);
const OpenGLTexture& targetTexture = static_cast<const OpenGLTexture&>(toTexture);
m_commandBuffer.CopyTexture(sourceTexture.GetTexture(), fromBox, targetTexture.GetTexture(), toPos);
m_commandBuffer.CopyTexture(sourceTexture, fromBox, targetTexture, toPos);
}
void OpenGLCommandBufferBuilder::Draw(UInt32 vertexCount, UInt32 instanceCount, UInt32 firstVertex, UInt32 firstInstance)

View File

@ -71,6 +71,9 @@ namespace Nz
if (m_referenceContext->glPolygonMode) //< not supported in core OpenGL ES, but supported in OpenGL or with GL_NV_polygon_mode extension
m_deviceInfo.features.nonSolidFaceFilling = true;
if (m_referenceContext->IsExtensionSupported(GL::Extension::TextureView))
m_deviceInfo.features.unrestrictedTextureViews = true;
// Limits
GLint minUboOffsetAlignment;
m_referenceContext->glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &minUboOffsetAlignment);

View File

@ -139,6 +139,8 @@ namespace Nz
if (const OpenGLTexture* glTexture = static_cast<const OpenGLTexture*>(textureBinding.texture))
{
// TODO: Add texture view emulation
textureDescriptor.texture = glTexture->GetTexture().GetObjectId();
if (const OpenGLTextureSampler* glSampler = static_cast<const OpenGLTextureSampler*>(textureBinding.sampler))

View File

@ -96,7 +96,7 @@ namespace Nz
const OpenGLTexture& glTexture = static_cast<const OpenGLTexture&>(source);
const GL::Context& context = m_texture.EnsureDeviceContext();
return context.CopyTexture(glTexture.GetTexture(), m_texture, srcBox, dstPos);
return context.CopyTexture(glTexture, *this, srcBox, dstPos);
}
std::shared_ptr<Texture> OpenGLTexture::CreateView(const TextureViewInfo& viewInfo)

View File

@ -280,28 +280,58 @@ namespace Nz::GL
}
}
bool Context::BlitTexture(const Texture& source, const Texture& destination, const Boxui& srcBox, const Boxui& dstBox, SamplerFilter filter) const
bool Context::BlitTexture(const OpenGLTexture& texture, const OpenGLTexture& destination, const Boxui& srcBox, const Boxui& dstBox, SamplerFilter filter) const
{
if (!m_blitFramebuffers && !InitializeBlitFramebuffers())
return false;
//TODO: handle other textures types
assert(source.GetTarget() == TextureTarget::Target2D);
assert(destination.GetTarget() == TextureTarget::Target2D);
// Bind framebuffers before configuring them (so they won't override each other)
BindFramebuffer(FramebufferTarget::Draw, m_blitFramebuffers->drawFBO.GetObjectId());
BindFramebuffer(FramebufferTarget::Read, m_blitFramebuffers->readFBO.GetObjectId());
auto BindTexture = [](GL::Framebuffer& framebuffer, const OpenGLTexture& texture)
{
if (texture.RequiresTextureViewEmulation())
{
const TextureViewInfo& texViewInfo = texture.GetTextureViewInfo();
GLenum texTarget;
if (texture.GetType() == ImageType::Cubemap)
{
constexpr std::array<GLenum, 6> faceTargets = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z };
assert(texViewInfo.baseArrayLayer < faceTargets.size());
texTarget = faceTargets[texViewInfo.baseArrayLayer];
}
else if (texture.GetType() == ImageType::E2D)
texTarget = GL_TEXTURE_2D;
else
throw std::runtime_error("unrestricted texture views are not supported on this device, blit is only permitted from/to a cubemap face or a 2D texture");
//TODO: Support texture arrays (one slice at a time)
framebuffer.Texture2D(GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.GetParentTexture()->GetTexture().GetObjectId(), texture.GetTextureViewInfo().baseMipLevel);
}
else
{
if (texture.GetTexture().GetTarget() != TextureTarget::Target2D)
throw std::runtime_error("blit is not yet supported from other texture type than 2D textures");
framebuffer.Texture2D(GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.GetTexture().GetObjectId(), 0);
}
};
// Attach textures to color attachment
m_blitFramebuffers->drawFBO.Texture2D(GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, destination.GetObjectId());
BindTexture(m_blitFramebuffers->readFBO, texture);
BindTexture(m_blitFramebuffers->drawFBO, destination);
// Validate framebuffer completeness
if (GLenum checkResult = m_blitFramebuffers->drawFBO.Check(); checkResult != GL_FRAMEBUFFER_COMPLETE)
{
NazaraError("Blit draw FBO is incomplete: " + TranslateOpenGLError(checkResult));
return false;
}
m_blitFramebuffers->readFBO.Texture2D(GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, source.GetObjectId());
if (GLenum checkResult = m_blitFramebuffers->readFBO.Check(); checkResult != GL_FRAMEBUFFER_COMPLETE)
{
NazaraError("Blit read FBO is incomplete: " + TranslateOpenGLError(checkResult));
@ -324,18 +354,20 @@ namespace Nz::GL
return true;
}
bool Context::CopyTexture(const Texture& source, const Texture& destination, const Boxui& srcBox, const Vector3ui& dstPos) const
bool Context::CopyTexture(const OpenGLTexture& source, const OpenGLTexture& destination, const Boxui& srcBox, const Vector3ui& dstPos) const
{
// Use glCopyImageSubData if available
if (glCopyImageSubData)
// TODO: Emulate texture views (which aren't available on GL ES, even though glCopyImageSubData is)
if (glCopyImageSubData && !source.RequiresTextureViewEmulation() && !destination.RequiresTextureViewEmulation())
{
GLuint srcImage = source.GetObjectId();
GLenum srcTarget = ToOpenGL(source.GetTarget());
GLuint srcImage = source.GetTexture().GetObjectId();
GLenum srcTarget = ToOpenGL(source.GetTexture().GetTarget());
GLuint dstImage = destination.GetObjectId();
GLenum dstTarget = ToOpenGL(destination.GetTarget());
GLuint dstImage = destination.GetTexture().GetObjectId();
GLenum dstTarget = ToOpenGL(destination.GetTexture().GetTarget());
glCopyImageSubData(srcImage, srcTarget, 0, GLint(srcBox.x), GLint(srcBox.y), GLint(srcBox.z), dstImage, dstTarget, 0, GLint(dstPos.x), GLint(dstPos.y), GLint(dstPos.z), GLsizei(srcBox.width), GLsizei(srcBox.height), GLsizei(srcBox.depth));
return true;
}
else

View File

@ -34,28 +34,19 @@ namespace Nz
void RenderDevice::ValidateFeatures(const RenderDeviceFeatures& supportedFeatures, RenderDeviceFeatures& enabledFeatures)
{
if (enabledFeatures.anisotropicFiltering && !supportedFeatures.anisotropicFiltering)
{
NazaraWarning("anistropic filtering was enabled but device doesn't support it, disabling...");
enabledFeatures.anisotropicFiltering = false;
#define NzValidateFeature(field, name) \
if (enabledFeatures.field && !supportedFeatures.field) \
{ \
NazaraWarning(name " was enabled but device doesn't support it, disabling..."); \
enabledFeatures.field = false; \
}
if (enabledFeatures.depthClamping && !supportedFeatures.depthClamping)
{
NazaraWarning("depth clamping was enabled but device doesn't support it, disabling...");
enabledFeatures.depthClamping = false;
}
NzValidateFeature(anisotropicFiltering, "anistropic filtering feature")
NzValidateFeature(depthClamping, "depth clamping feature")
NzValidateFeature(nonSolidFaceFilling, "non-solid face filling feature")
NzValidateFeature(storageBuffers, "storage buffers support")
NzValidateFeature(unrestrictedTextureViews, "unrestricted texture view support")
if (enabledFeatures.nonSolidFaceFilling && !supportedFeatures.nonSolidFaceFilling)
{
NazaraWarning("non-solid face filling was enabled but device doesn't support it, disabling...");
enabledFeatures.nonSolidFaceFilling = false;
}
if (enabledFeatures.storageBuffers && !supportedFeatures.storageBuffers)
{
NazaraWarning("storage buffers support was enabled but device doesn't support it, disabling...");
enabledFeatures.storageBuffers = false;
}
#undef NzValidateFeature
}
}

View File

@ -57,6 +57,7 @@ namespace Nz
deviceInfo.features.depthClamping = physDevice.features.depthClamp;
deviceInfo.features.nonSolidFaceFilling = physDevice.features.fillModeNonSolid;
deviceInfo.features.storageBuffers = true;
deviceInfo.features.unrestrictedTextureViews = true;
deviceInfo.limits.maxStorageBufferSize = physDevice.properties.limits.maxStorageBufferRange;
deviceInfo.limits.maxUniformBufferSize = physDevice.properties.limits.maxUniformBufferRange;

View File

@ -114,15 +114,17 @@ namespace Nz
CallOnExit releaseImage([&]{ vmaDestroyImage(m_device.GetMemoryAllocator(), m_image, m_allocation); });
createInfoView.subresourceRange = {
// Create default view (viewing the whole texture)
m_imageRange = {
ToVulkan(PixelFormatInfo::GetContent(textureInfo.pixelFormat)),
0, //< baseMipLevel
createInfo.mipLevels, //< levelCount
0, //< baseArrayLayer
createInfo.arrayLayers //< layerCount, will be set if the type is an array/cubemap
createInfo.arrayLayers //< layerCount
};
createInfoView.image = m_image;
createInfoView.subresourceRange = m_imageRange;
if (!m_imageView.Create(m_device, createInfoView))
throw std::runtime_error("Failed to create default image view: " + TranslateVulkanError(m_imageView.GetLastErrorCode()));
@ -141,16 +143,19 @@ namespace Nz
NazaraAssert(viewInfo.layerCount <= m_parentTexture->m_textureInfo.layerCount - viewInfo.baseArrayLayer, "layer count exceeds number of layers");
NazaraAssert(viewInfo.levelCount <= m_parentTexture->m_textureInfo.levelCount - viewInfo.baseMipLevel, "level count exceeds number of levels");
m_viewInfo = viewInfo;
m_imageRange = {
ToVulkan(PixelFormatInfo::GetContent(viewInfo.reinterpretFormat)),
viewInfo.baseMipLevel,
viewInfo.levelCount,
viewInfo.baseArrayLayer,
viewInfo.layerCount
};
VkImageViewCreateInfo createInfoView = {};
createInfoView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfoView.image = m_image;
createInfoView.subresourceRange = {
ToVulkan(PixelFormatInfo::GetContent(m_textureInfo.pixelFormat)),
0, //< baseMipLevel
m_textureInfo.levelCount, //< levelCount
0, //< baseArrayLayer
m_textureInfo.layerCount //< layerCount, will be set if the type is an array/cubemap
};
createInfoView.subresourceRange = m_imageRange;
InitViewForFormat(viewInfo.reinterpretFormat, createInfoView);
@ -218,7 +223,17 @@ namespace Nz
std::shared_ptr<Texture> VulkanTexture::CreateView(const TextureViewInfo& viewInfo)
{
if (m_parentTexture)
return m_parentTexture->CreateView(viewInfo);
{
assert(m_viewInfo);
NazaraAssert(viewInfo.layerCount <= m_viewInfo->layerCount - viewInfo.baseArrayLayer, "layer count exceeds number of layers");
NazaraAssert(viewInfo.levelCount <= m_viewInfo->levelCount - viewInfo.baseMipLevel, "level count exceeds number of levels");
TextureViewInfo ajustedView = viewInfo;
ajustedView.baseArrayLayer += m_viewInfo->baseArrayLayer;
ajustedView.baseMipLevel += m_viewInfo->baseMipLevel;
return m_parentTexture->CreateView(ajustedView);
}
return std::make_shared<VulkanTexture>(std::static_pointer_cast<VulkanTexture>(shared_from_this()), viewInfo);
}
@ -329,13 +344,13 @@ namespace Nz
VkImageSubresourceLayers subresourceLayers = { //< FIXME
aspect,
level, //< mipLevel
0, //< baseArrayLayer
m_imageRange.baseArrayLayer, //< baseArrayLayer
UInt32((m_textureInfo.type == ImageType::Cubemap) ? 6 : 1) //< layerCount
};
VkImageSubresourceRange subresourceRange = { //< FIXME
aspect,
0, //< baseMipLevel
m_imageRange.baseMipLevel, //< baseMipLevel
1, //< levelCount
subresourceLayers.baseArrayLayer, //< baseArrayLayer
subresourceLayers.layerCount //< layerCount
@ -379,13 +394,6 @@ namespace Nz
void VulkanTexture::InitViewForFormat(PixelFormat pixelFormat, VkImageViewCreateInfo& createImageView)
{
createImageView.components = {
VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_B,
VK_COMPONENT_SWIZZLE_A
};
// TODO: Fill this switch
switch (pixelFormat)
{
@ -407,6 +415,13 @@ namespace Nz
case PixelFormat::RGBA32F:
{
createImageView.format = ToVulkan(pixelFormat);
createImageView.components = {
VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_B,
VK_COMPONENT_SWIZZLE_A
};
break;
}
@ -450,14 +465,6 @@ namespace Nz
default:
throw std::runtime_error("Unsupported pixel format " + PixelFormatInfo::GetName(pixelFormat));
}
createImageView.subresourceRange = {
ToVulkan(PixelFormatInfo::GetContent(pixelFormat)),
0,
1,
0,
1
};
}
}