NazaraEngine/src/Nazara/Renderer/RenderTexture.cpp

846 lines
21 KiB
C++

// 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 <Nazara/Renderer/RenderTexture.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Renderer/Context.hpp>
#include <Nazara/Renderer/OpenGL.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RenderBuffer.hpp>
#include <Nazara/Renderer/Texture.hpp>
#include <Nazara/Utility/PixelFormat.hpp>
#include <functional>
#include <limits>
#include <memory>
#include <vector>
#include <Nazara/Renderer/Debug.hpp>
namespace
{
struct Attachment
{
NazaraSlot(NzRenderBuffer, OnRenderBufferDestroy, renderBufferDestroySlot);
NazaraSlot(NzTexture, OnTextureDestroy, textureDestroySlot);
NzRenderBufferRef buffer;
NzTextureRef texture;
nzAttachmentPoint attachmentPoint;
bool isBuffer;
bool isUsed = false;
unsigned int height;
unsigned int width;
};
unsigned int attachmentIndex[nzAttachmentPoint_Max+1] =
{
3, // nzAttachmentPoint_Color
0, // nzAttachmentPoint_Depth
1, // nzAttachmentPoint_DepthStencil
2 // nzAttachmentPoint_Stencil
};
nzAttachmentPoint formatTypeToAttachment[nzPixelFormatType_Max+1] =
{
nzAttachmentPoint_Color, // nzPixelFormatType_Color
nzAttachmentPoint_Depth, // nzPixelFormatType_Depth
nzAttachmentPoint_DepthStencil, // nzPixelFormatType_DepthStencil
nzAttachmentPoint_Stencil // nzPixelFormatType_Stencil
};
GLuint lockedPrevious = 0;
nzUInt8 lockedLevel = 0;
}
struct NzRenderTextureImpl
{
NazaraSlot(NzContext, OnContextDestroy, contextDestroySlot);
GLuint fbo;
std::vector<Attachment> attachments;
std::vector<nzUInt8> colorTargets;
mutable std::vector<GLenum> drawBuffers;
const NzContext* context;
bool complete = false;
bool userDefinedTargets = false;
unsigned int height;
unsigned int width;
};
bool NzRenderTexture::AttachBuffer(nzAttachmentPoint attachmentPoint, nzUInt8 index, NzRenderBuffer* buffer)
{
#if NAZARA_RENDERER_SAFE
if (!m_impl)
{
NazaraError("Render texture not created");
return false;
}
if (attachmentPoint != nzAttachmentPoint_Color)
{
if (index > 0)
{
NazaraError("Index must be 0 for non-color attachments");
return false;
}
}
else
{
if (index >= NzRenderer::GetMaxColorAttachments())
{
NazaraError("Color index is over max color attachments (" + NzString::Number(index) + " >= " + NzString::Number(NzRenderer::GetMaxColorAttachments()) + ")");
return false;
}
}
if (!buffer || !buffer->IsValid())
{
NazaraError("Invalid render buffer");
return false;
}
unsigned int depthStencilIndex = attachmentIndex[nzAttachmentPoint_DepthStencil];
if (m_impl->attachments.size() > depthStencilIndex && m_impl->attachments[depthStencilIndex].isUsed)
{
if (attachmentPoint == nzAttachmentPoint_Depth)
{
NazaraError("Depth target already attached by DepthStencil attachment");
return false;
}
else if (attachmentPoint == nzAttachmentPoint_Stencil)
{
NazaraError("Stencil target already attached by DepthStencil attachment");
return false;
}
}
nzAttachmentPoint targetAttachmentPoint = formatTypeToAttachment[NzPixelFormat::GetType(buffer->GetFormat())];
if (targetAttachmentPoint != attachmentPoint && targetAttachmentPoint != nzAttachmentPoint_DepthStencil &&
attachmentPoint != nzAttachmentPoint_Depth && attachmentPoint != nzAttachmentPoint_Stencil)
{
NazaraError("Pixel format type does not match attachment point type");
return false;
}
#endif
if (!Lock())
{
NazaraError("Failed to lock render texture");
return false;
}
// Détachement de l'attache précédente (Si il y a)
Detach(attachmentPoint, index);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, GL_RENDERBUFFER, buffer->GetOpenGLID());
Unlock();
unsigned int attachIndex = attachmentIndex[attachmentPoint] + index;
if (attachIndex >= m_impl->attachments.size())
m_impl->attachments.resize(attachIndex+1);
Attachment& attachment = m_impl->attachments[attachIndex];
attachment.attachmentPoint = attachmentPoint;
attachment.buffer = buffer;
attachment.renderBufferDestroySlot.Connect(buffer->OnRenderBufferDestroy, std::bind(&NzRenderTexture::OnRenderBufferDestroy, this, std::placeholders::_1, attachIndex));
attachment.isBuffer = true;
attachment.isUsed = true;
attachment.height = buffer->GetHeight();
attachment.width = buffer->GetWidth();
InvalidateSize();
InvalidateTargets();
if (attachmentPoint == nzAttachmentPoint_Color && !m_impl->userDefinedTargets)
m_impl->colorTargets.push_back(index);
return true;
}
bool NzRenderTexture::AttachBuffer(nzAttachmentPoint attachmentPoint, nzUInt8 index, nzPixelFormat format, unsigned int width, unsigned int height)
{
NzRenderBufferRef renderBuffer = NzRenderBuffer::New();
if (!renderBuffer->Create(format, width, height))
{
NazaraError("Failed to create RenderBuffer");
return false;
}
if (!AttachBuffer(attachmentPoint, index, renderBuffer))
{
NazaraError("Failed to attach buffer");
return false;
}
return true;
}
bool NzRenderTexture::AttachTexture(nzAttachmentPoint attachmentPoint, nzUInt8 index, NzTexture* texture, unsigned int z)
{
#if NAZARA_RENDERER_SAFE
if (!m_impl)
{
NazaraError("Render texture not created");
return false;
}
if (attachmentPoint != nzAttachmentPoint_Color)
{
if (index > 0)
{
NazaraError("Index must be 0 for non-color attachments");
return false;
}
}
else
{
if (index >= NzRenderer::GetMaxColorAttachments())
{
NazaraError("Color index is over max color attachments (" + NzString::Number(index) + " >= " + NzString::Number(NzRenderer::GetMaxColorAttachments()) + ")");
return false;
}
}
if (attachmentPoint == nzAttachmentPoint_Stencil)
{
NazaraError("Targeting stencil-only textures is not supported");
return false;
}
unsigned int depthStencilIndex = attachmentIndex[nzAttachmentPoint_DepthStencil];
if (attachmentPoint == nzAttachmentPoint_Depth && m_impl->attachments.size() > depthStencilIndex &&
m_impl->attachments[depthStencilIndex].isUsed)
{
NazaraError("Depth target already attached by DepthStencil attachment");
return false;
}
if (!texture || !texture->IsValid())
{
NazaraError("Invalid texture");
return false;
}
unsigned int depth = (texture->GetType() == nzImageType_Cubemap) ? 6 : texture->GetDepth();
if (z >= depth)
{
NazaraError("Z value exceeds depth (" + NzString::Number(z) + " >= (" + NzString::Number(depth) + ')');
return false;
}
nzAttachmentPoint targetAttachmentPoint = formatTypeToAttachment[NzPixelFormat::GetType(texture->GetFormat())];
if (targetAttachmentPoint != attachmentPoint && targetAttachmentPoint != nzAttachmentPoint_DepthStencil &&
attachmentPoint != nzAttachmentPoint_Depth && attachmentPoint != nzAttachmentPoint_Stencil)
{
NazaraError("Pixel format type does not match attachment point type");
return false;
}
#endif
if (!Lock())
{
NazaraError("Failed to lock render texture");
return false;
}
// Détachement de l'attache précédente (Si il y a)
Detach(attachmentPoint, index);
switch (texture->GetType())
{
case nzImageType_1D:
glFramebufferTexture1D(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, GL_TEXTURE_1D, texture->GetOpenGLID(), 0);
break;
case nzImageType_1D_Array:
case nzImageType_2D_Array:
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, texture->GetOpenGLID(), 0, z);
break;
case nzImageType_2D:
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, GL_TEXTURE_2D, texture->GetOpenGLID(), 0);
break;
case nzImageType_3D:
glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, GL_TEXTURE_3D, texture->GetOpenGLID(), 0, z);
break;
case nzImageType_Cubemap:
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, NzOpenGL::CubemapFace[z], texture->GetOpenGLID(), 0);
break;
}
Unlock();
unsigned int attachIndex = attachmentIndex[attachmentPoint] + index;
if (attachIndex >= m_impl->attachments.size())
m_impl->attachments.resize(attachIndex+1);
Attachment& attachment = m_impl->attachments[attachIndex];
attachment.attachmentPoint = attachmentPoint;
attachment.isBuffer = false;
attachment.isUsed = true;
attachment.height = texture->GetHeight();
attachment.texture = texture;
attachment.textureDestroySlot.Connect(texture->OnTextureDestroy, std::bind(&NzRenderTexture::OnTextureDestroy, this, std::placeholders::_1, attachIndex));
attachment.width = texture->GetWidth();
InvalidateSize();
InvalidateTargets();
if (attachmentPoint == nzAttachmentPoint_Color && !m_impl->userDefinedTargets)
m_impl->colorTargets.push_back(index);
return true;
}
bool NzRenderTexture::Create(bool lock)
{
if (!IsSupported())
{
NazaraError("Render textures not supported");
return false;
}
Destroy();
#if NAZARA_RENDERER_SAFE
if (NzContext::GetCurrent() == nullptr)
{
NazaraError("No active context");
return false;
}
#endif
std::unique_ptr<NzRenderTextureImpl> impl(new NzRenderTextureImpl);
impl->fbo = 0;
glGenFramebuffers(1, &impl->fbo);
if (!impl->fbo)
{
NazaraError("Failed to create framebuffer");
return false;
}
m_impl = impl.release();
m_impl->context = NzContext::GetCurrent();
m_impl->contextDestroySlot.Connect(m_impl->context->OnContextDestroy, this, &NzRenderTexture::OnContextDestroy);
m_checked = false;
m_drawBuffersUpdated = true;
m_sizeUpdated = false;
m_targetsUpdated = true;
if (lock)
{
// En cas d'exception, la ressource sera quand même libérée
NzCallOnExit onExit([this] ()
{
Destroy();
});
if (!Lock())
{
NazaraError("Failed to lock render texture");
return false;
}
onExit.Reset();
}
OnRenderTargetParametersChange(this);
OnRenderTargetSizeChange(this);
return true;
}
void NzRenderTexture::Destroy()
{
if (m_impl)
{
if (IsActive())
NzRenderer::SetTarget(nullptr);
// Le FBO devant être supprimé dans son contexte d'origine, nous déléguons sa suppression à la classe OpenGL
// Celle-ci va libérer le FBO dès que possible (la prochaine fois que son contexte d'origine sera actif)
NzOpenGL::DeleteFrameBuffer(m_impl->context, m_impl->fbo);
delete m_impl; // Enlève également une références sur les Texture/RenderBuffer
m_impl = nullptr;
}
}
void NzRenderTexture::Detach(nzAttachmentPoint attachmentPoint, nzUInt8 index)
{
#if NAZARA_RENDERER_SAFE
if (!m_impl)
{
NazaraError("Render texture not created");
return;
}
if (attachmentPoint != nzAttachmentPoint_Color && index > 0)
{
NazaraError("Index must be 0 for non-color attachments");
return;
}
#endif
unsigned int attachIndex = attachmentIndex[attachmentPoint] + index;
if (attachIndex >= m_impl->attachments.size())
return;
Attachment& attachement = m_impl->attachments[attachIndex];
if (!attachement.isUsed)
return;
if (!Lock())
{
NazaraError("Failed to lock render texture");
return;
}
attachement.isUsed = false;
if (attachement.isBuffer)
{
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, GL_RENDERBUFFER, 0);
attachement.buffer = nullptr;
attachement.renderBufferDestroySlot.Disconnect();
}
else
{
if (glFramebufferTexture)
glFramebufferTexture(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, 0, 0);
else
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, NzOpenGL::Attachment[attachmentPoint]+index, 0, 0, 0);
attachement.texture = nullptr;
attachement.textureDestroySlot.Disconnect();
}
InvalidateSize();
if (attachement.attachmentPoint == nzAttachmentPoint_Color)
InvalidateTargets();
Unlock();
m_checked = false;
}
unsigned int NzRenderTexture::GetHeight() const
{
NazaraAssert(m_impl, "Invalid render texture");
if (!m_sizeUpdated)
UpdateSize();
return m_impl->height;
}
NzRenderTargetParameters NzRenderTexture::GetParameters() const
{
NazaraAssert(m_impl, "Invalid render texture");
///TODO
return NzRenderTargetParameters();
}
NzVector2ui NzRenderTexture::GetSize() const
{
NazaraAssert(m_impl, "Invalid render texture");
if (!m_sizeUpdated)
UpdateSize();
return NzVector2ui(m_impl->width, m_impl->height);
}
unsigned int NzRenderTexture::GetWidth() const
{
NazaraAssert(m_impl, "Invalid render texture");
if (!m_sizeUpdated)
UpdateSize();
return m_impl->width;
}
bool NzRenderTexture::IsComplete() const
{
NazaraAssert(m_impl, "Invalid render texture");
if (!m_checked)
{
if (!Lock())
{
NazaraError("Failed to lock render texture");
return false;
}
GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
Unlock();
m_impl->complete = false;
switch (status)
{
case GL_FRAMEBUFFER_COMPLETE:
m_impl->complete = true;
break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
NazaraError("Incomplete attachment");
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
NazaraInternalError("Incomplete draw buffer");
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
NazaraInternalError("Incomplete read buffer");
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
NazaraError("Incomplete missing attachment");
break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
NazaraError("Incomplete multisample");
break;
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
NazaraError("Incomplete layer targets");
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
NazaraError("Render texture has unsupported attachments");
break;
default:
NazaraInternalError("Unknown error");
}
m_checked = true;
}
return m_impl->complete;
}
bool NzRenderTexture::IsRenderable() const
{
return IsComplete() && !m_impl->attachments.empty();
}
bool NzRenderTexture::Lock() const
{
NazaraAssert(m_impl, "Invalid render texture");
#if NAZARA_RENDERER_SAFE
if (NzContext::GetCurrent() != m_impl->context)
{
NazaraError("RenderTexture cannot be used with this context");
return false;
}
#endif
if (lockedLevel++ == 0)
{
GLint previous;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous);
lockedPrevious = previous;
if (lockedPrevious != m_impl->fbo)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_impl->fbo);
}
return true;
}
void NzRenderTexture::SetColorTargets(const nzUInt8* targets, unsigned int targetCount) const
{
NazaraAssert(m_impl, "Invalid render texture");
#if NAZARA_RENDERER_SAFE
for (unsigned int i = 0; i < targetCount; ++i)
{
unsigned int index = attachmentIndex[nzAttachmentPoint_Color] + targets[i];
if (index >= m_impl->attachments.size() || !m_impl->attachments[index].isUsed)
{
NazaraError("Target " + NzString::Number(targets[i]) + " not attached");
return;
}
}
#endif
m_impl->colorTargets.resize(targetCount);
std::memcpy(&m_impl->colorTargets[0], targets, targetCount*sizeof(nzUInt8));
m_impl->userDefinedTargets = true;
InvalidateDrawBuffers();
}
void NzRenderTexture::SetColorTargets(const std::initializer_list<nzUInt8>& targets) const
{
NazaraAssert(m_impl, "Invalid render texture");
#if NAZARA_RENDERER_SAFE
for (nzUInt8 target : targets)
{
unsigned int index = attachmentIndex[nzAttachmentPoint_Color] + target;
if (index >= m_impl->attachments.size() || !m_impl->attachments[index].isUsed)
{
NazaraError("Target " + NzString::Number(target) + " not attached");
return;
}
}
#endif
m_impl->colorTargets.resize(targets.size());
nzUInt8* ptr = &m_impl->colorTargets[0];
for (nzUInt8 index : targets)
*ptr++ = index;
m_impl->userDefinedTargets = true;
InvalidateDrawBuffers();
}
void NzRenderTexture::Unlock() const
{
NazaraAssert(m_impl, "Invalid render texture");
#if NAZARA_RENDERER_SAFE
if (NzContext::GetCurrent() != m_impl->context)
{
NazaraError("RenderTexture cannot be used with this context");
return;
}
if (lockedLevel == 0)
{
NazaraWarning("Unlock called on non-locked texture");
return;
}
#endif
if (--lockedLevel == 0 && lockedPrevious != m_impl->fbo) // Ici, il est important qu'un FBO soit débindé si l'ancien était 0
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, lockedPrevious);
}
unsigned int NzRenderTexture::GetOpenGLID() const
{
NazaraAssert(m_impl, "Invalid render texture");
#if NAZARA_RENDERER_SAFE
if (NzContext::GetCurrent() != m_impl->context)
{
NazaraError("RenderTexture cannot be used with this context");
return 0;
}
#endif
return m_impl->fbo;
}
bool NzRenderTexture::HasContext() const
{
return false;
}
bool NzRenderTexture::IsSupported()
{
return NzOpenGL::IsSupported(nzOpenGLExtension_FrameBufferObject);
}
void NzRenderTexture::Blit(NzRenderTexture* src, NzRectui srcRect, NzRenderTexture* dst, NzRectui dstRect, nzUInt32 buffers, bool bilinearFilter)
{
NazaraAssert(src && src->IsValid(), "Invalid source render texture");
NazaraAssert(dst && dst->IsValid(), "Invalid destination render texture");
#if NAZARA_RENDERER_SAFE
if (srcRect.x+srcRect.width > src->GetWidth() || srcRect.y+srcRect.height > src->GetHeight())
{
NazaraError("Source rectangle dimensions are out of bounds");
return;
}
if (dstRect.x+dstRect.width > dst->GetWidth() || dstRect.y+dstRect.height > dst->GetHeight())
{
NazaraError("Destination rectangle dimensions are out of bounds");
return;
}
if (bilinearFilter && (buffers & nzRendererBuffer_Depth || buffers & nzRendererBuffer_Stencil))
{
NazaraError("Filter cannot be bilinear when blitting depth/stencil buffers");
return;
}
#endif
GLbitfield mask = 0;
if (buffers & nzRendererBuffer_Color)
mask |= GL_COLOR_BUFFER_BIT;
if (buffers & nzRendererBuffer_Depth)
mask |= GL_DEPTH_BUFFER_BIT;
if (buffers & nzRendererBuffer_Stencil)
mask |= GL_STENCIL_BUFFER_BIT;
GLint previousDrawBuffer, previousReadBuffer;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previousDrawBuffer);
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &previousReadBuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->GetOpenGLID());
glBindFramebuffer(GL_READ_FRAMEBUFFER, src->GetOpenGLID());
glBlitFramebuffer(srcRect.x, srcRect.y, srcRect.x + srcRect.width, srcRect.y + srcRect.height,
dstRect.x, dstRect.y, dstRect.x + dstRect.width, dstRect.y + dstRect.height,
mask, (bilinearFilter) ? GL_LINEAR : GL_NEAREST);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previousDrawBuffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER, previousReadBuffer);
}
bool NzRenderTexture::Activate() const
{
NazaraAssert(m_impl, "Invalid render texture");
#if NAZARA_RENDERER_SAFE
if (NzContext::GetCurrent() != m_impl->context)
{
NazaraError("RenderTexture cannot be used with this context");
return false;
}
#endif
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_impl->fbo);
m_drawBuffersUpdated = false;
return true;
}
void NzRenderTexture::Desactivate() const
{
NazaraAssert(m_impl, "Invalid render texture");
#if NAZARA_RENDERER_SAFE
if (NzContext::GetCurrent() != m_impl->context)
{
NazaraError("RenderTexture cannot be used with this context");
return;
}
#endif
glFlush();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
void NzRenderTexture::EnsureTargetUpdated() const
{
if (!m_drawBuffersUpdated)
UpdateDrawBuffers();
for (nzUInt8 index : m_impl->colorTargets)
{
Attachment& attachment = m_impl->attachments[attachmentIndex[nzAttachmentPoint_Color] + index];
if (!attachment.isBuffer)
attachment.texture->InvalidateMipmaps();
}
}
void NzRenderTexture::OnContextDestroy(const NzContext* context)
{
NazaraAssert(m_impl, "Invalid internal state");
NazaraUnused(context);
#ifdef NAZARA_DEBUG
if (m_impl->context != context)
{
NazaraInternalError("Not listening to " + NzString::Pointer(context));
return;
}
#endif
Destroy();
}
void NzRenderTexture::OnRenderBufferDestroy(const NzRenderBuffer* renderBuffer, unsigned int attachmentIndex)
{
NazaraAssert(m_impl, "Invalid internal state");
NazaraAssert(attachmentIndex < m_impl->attachments.size(), "Invalid attachment index");
NazaraAssert(m_impl->attachments[attachmentIndex].isBuffer, "Invalid attachment state");
NazaraUnused(renderBuffer);
Attachment& attachment = m_impl->attachments[attachmentIndex];
attachment.buffer = nullptr;
attachment.isUsed = false;
attachment.renderBufferDestroySlot.Disconnect();
InvalidateTargets();
}
void NzRenderTexture::OnTextureDestroy(const NzTexture* texture, unsigned int attachmentIndex)
{
NazaraAssert(m_impl, "Invalid internal state");
NazaraAssert(attachmentIndex < m_impl->attachments.size(), "Invalid attachment index");
NazaraAssert(!m_impl->attachments[attachmentIndex].isBuffer, "Invalid attachment state");
NazaraUnused(texture);
InvalidateTargets();
}
void NzRenderTexture::UpdateDrawBuffers() const
{
if (!m_targetsUpdated)
UpdateTargets();
glDrawBuffers(m_impl->drawBuffers.size(), &m_impl->drawBuffers[0]);
m_drawBuffersUpdated = true;
}
void NzRenderTexture::UpdateSize() const
{
m_impl->width = 0;
m_impl->height = 0;
for (Attachment& attachment : m_impl->attachments)
{
if (attachment.isUsed)
{
m_impl->height = std::max(m_impl->height, attachment.height);
m_impl->width = std::max(m_impl->width, attachment.width);
}
}
m_sizeUpdated = true;
}
void NzRenderTexture::UpdateTargets() const
{
if (m_impl->colorTargets.empty())
{
m_impl->drawBuffers.resize(1);
m_impl->drawBuffers[0] = GL_NONE;
}
else
{
m_impl->drawBuffers.resize(m_impl->colorTargets.size());
GLenum* ptr = &m_impl->drawBuffers[0];
for (nzUInt8 index : m_impl->colorTargets)
*ptr++ = GL_COLOR_ATTACHMENT0 + index;
}
m_targetsUpdated = true;
}