diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 90644d8a652..2a2322f7223 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -28,6 +28,9 @@ #include #include +#include +#include + #include namespace filament { @@ -806,6 +809,10 @@ BufferDescriptor b(nullptr, 0u, [](void* buffer, size_t size, void* user) { MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint32_t height, uint8_t samples, Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT], Attachment depthAttachment) : HwRenderTarget(width, height), context(context), samples(samples) { + UTILS_UNUSED_IN_RELEASE math::vec2 tmin = {std::numeric_limits::max()}; + UTILS_UNUSED_IN_RELEASE math::vec2 tmax = {0}; + UTILS_UNUSED_IN_RELEASE size_t attachmentCount = 0; + for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { if (!colorAttachments[i]) { continue; @@ -816,6 +823,13 @@ BufferDescriptor b(nullptr, 0u, [](void* buffer, size_t size, void* user) { "MetalRenderTarget was initialized with a MSAA COLOR%d texture, but sample count is %d.", i, samples); + auto t = color[i].metalTexture; + const auto twidth = std::max(1u, t->width >> color[i].level); + const auto theight = std::max(1u, t->height >> color[i].level); + tmin = { std::min(tmin.x, twidth), std::min(tmin.y, theight) }; + tmax = { std::max(tmax.x, twidth), std::max(tmax.y, theight) }; + attachmentCount++; + // If we were given a single-sampled texture but the samples parameter is > 1, we create // a multisampled sidecar texture and do a resolve automatically. if (samples > 1 && color[i].getSampleCount() == 1) { @@ -834,6 +848,13 @@ BufferDescriptor b(nullptr, 0u, [](void* buffer, size_t size, void* user) { "MetalRenderTarget was initialized with a MSAA DEPTH texture, but sample count is %d.", samples); + auto t = depth.metalTexture; + const auto twidth = std::max(1u, t->width >> depth.level); + const auto theight = std::max(1u, t->height >> depth.level); + tmin = { math::min(tmin.x, twidth), math::min(tmin.y, theight) }; + tmax = { math::max(tmax.x, twidth), math::max(tmax.y, theight) }; + attachmentCount++; + // If we were given a single-sampled texture but the samples parameter is > 1, we create // a multisampled sidecar texture and do a resolve automatically. if (samples > 1 && depth.getSampleCount() == 1) { @@ -844,6 +865,10 @@ BufferDescriptor b(nullptr, 0u, [](void* buffer, size_t size, void* user) { } } } + + // Verify that all attachments have the same dimensions. + assert_invariant(attachmentCount > 0); + assert_invariant(tmin == tmax); } void MetalRenderTarget::setUpRenderPassAttachments(MTLRenderPassDescriptor* descriptor, diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 19709fc9f2f..dbf5f4473fc 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -1076,12 +1076,17 @@ void OpenGLDriver::createRenderTargetR(Handle rth, rt->gl.samples = samples; rt->targets = targets; + UTILS_UNUSED_IN_RELEASE math::vec2 tmin = {std::numeric_limits::max()}; + UTILS_UNUSED_IN_RELEASE math::vec2 tmax = {0}; + if (any(targets & TargetBufferFlags::COLOR_ALL)) { GLenum bufs[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = { GL_NONE }; const size_t maxDrawBuffers = getMaxDrawBuffers(); for (size_t i = 0; i < maxDrawBuffers; i++) { if (any(targets & getTargetBufferFlagsAt(i))) { - rt->gl.color[i] = handle_cast(color[i].handle); + auto t = rt->gl.color[i] = handle_cast(color[i].handle); + tmin = { std::min(tmin.x, t->width), std::min(tmin.y, t->height) }; + tmax = { std::max(tmax.x, t->width), std::max(tmax.y, t->height) }; framebufferTexture(color[i], rt, GL_COLOR_ATTACHMENT0 + i); bufs[i] = GL_COLOR_ATTACHMENT0 + i; } @@ -1094,7 +1099,9 @@ void OpenGLDriver::createRenderTargetR(Handle rth, bool specialCased = false; if ((targets & TargetBufferFlags::DEPTH_AND_STENCIL) == TargetBufferFlags::DEPTH_AND_STENCIL) { assert_invariant(!stencil.handle || stencil.handle == depth.handle); - rt->gl.depth = handle_cast(depth.handle); + auto t = rt->gl.depth = handle_cast(depth.handle); + tmin = { std::min(tmin.x, t->width), std::min(tmin.y, t->height) }; + tmax = { std::max(tmax.x, t->width), std::max(tmax.y, t->height) }; if (any(rt->gl.depth->usage & TextureUsage::SAMPLEABLE) || (!depth.handle && !stencil.handle)) { // special case: depth & stencil requested, and both provided as the same texture @@ -1106,15 +1113,23 @@ void OpenGLDriver::createRenderTargetR(Handle rth, if (!specialCased) { if (any(targets & TargetBufferFlags::DEPTH)) { - rt->gl.depth = handle_cast(depth.handle); + auto t = rt->gl.depth = handle_cast(depth.handle); + tmin = { std::min(tmin.x, t->width), std::min(tmin.y, t->height) }; + tmax = { std::max(tmax.x, t->width), std::max(tmax.y, t->height) }; framebufferTexture(depth, rt, GL_DEPTH_ATTACHMENT); } if (any(targets & TargetBufferFlags::STENCIL)) { - rt->gl.stencil = handle_cast(stencil.handle); + auto t = rt->gl.stencil = handle_cast(stencil.handle); + tmin = { std::min(tmin.x, t->width), std::min(tmin.y, t->height) }; + tmax = { std::max(tmax.x, t->width), std::max(tmax.y, t->height) }; framebufferTexture(stencil, rt, GL_STENCIL_ATTACHMENT); } } + // Verify that all attachments have the same dimensions. + assert_invariant(any(targets & TargetBufferFlags::ALL)); + assert_invariant(tmin == tmax); + CHECK_GL_ERROR(utils::slog.e) } diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 43bb1961134..f98a0e6419c 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -539,6 +539,10 @@ void VulkanDriver::createDefaultRenderTargetR(Handle rth, int) { void VulkanDriver::createRenderTargetR(Handle rth, TargetBufferFlags targets, uint32_t width, uint32_t height, uint8_t samples, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) { + UTILS_UNUSED_IN_RELEASE math::vec2 tmin = {std::numeric_limits::max()}; + UTILS_UNUSED_IN_RELEASE math::vec2 tmax = {0}; + UTILS_UNUSED_IN_RELEASE size_t attachmentCount = 0; + VulkanAttachment colorTargets[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {}; for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { if (color[i].handle) { @@ -548,7 +552,9 @@ void VulkanDriver::createRenderTargetR(Handle rth, .layer = color[i].layer, }; UTILS_UNUSED_IN_RELEASE VkExtent2D extent = colorTargets[i].getExtent2D(); - assert_invariant(extent.width >= width && extent.height >= height); + tmin = { std::min(tmin.x, extent.width), std::min(tmin.y, extent.height) }; + tmax = { std::max(tmax.x, extent.width), std::max(tmax.y, extent.height) }; + attachmentCount++; } } @@ -560,7 +566,9 @@ void VulkanDriver::createRenderTargetR(Handle rth, .layer = depth.layer, }; UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[0].getExtent2D(); - assert_invariant(extent.width >= width && extent.height >= height); + tmin = { std::min(tmin.x, extent.width), std::min(tmin.y, extent.height) }; + tmax = { std::max(tmax.x, extent.width), std::max(tmax.y, extent.height) }; + attachmentCount++; } if (stencil.handle) { @@ -570,9 +578,17 @@ void VulkanDriver::createRenderTargetR(Handle rth, .layer = stencil.layer, }; UTILS_UNUSED_IN_RELEASE VkExtent2D extent = depthStencil[1].getExtent2D(); - assert_invariant(extent.width >= width && extent.height >= height); + tmin = { std::min(tmin.x, extent.width), std::min(tmin.y, extent.height) }; + tmax = { std::max(tmax.x, extent.width), std::max(tmax.y, extent.height) }; + attachmentCount++; } + // All attachments must have the same dimensions, which must be greater than or equal to the + // render target dimensions. + assert_invariant(attachmentCount > 0); + assert_invariant(tmin == tmax); + assert_invariant(tmin.x >= width && tmin.y >= height); + auto renderTarget = construct(rth, mContext, width, height, samples, colorTargets, depthStencil, mStagePool); mDisposer.createDisposable(renderTarget, [this, rth] () { diff --git a/filament/src/ResourceAllocator.cpp b/filament/src/ResourceAllocator.cpp index 319955927d3..6dcb1556bcb 100644 --- a/filament/src/ResourceAllocator.cpp +++ b/filament/src/ResourceAllocator.cpp @@ -129,19 +129,6 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name, uint32_t width, uint32_t height, uint32_t depth, std::array swizzle, backend::TextureUsage usage) noexcept { - - // Some WebGL implementations complain about an incomplete framebuffer when the attachment sizes - // are heterogeneous. This merits further investigation. -#if !defined(__EMSCRIPTEN__) - if (!(usage & TextureUsage::SAMPLEABLE)) { - // If this texture is not going to be sampled, we can round its size up - // this helps prevent many reallocations for small size changes. - // We round to 16 pixels, which works for 720p btw. - width = (width + 15u) & ~15u; - height = (height + 15u) & ~15u; - } -#endif - // The frame graph descriptor uses "0" to mean "auto" but the sample count that is passed to the // backend should always be 1 or greater. samples = samples ? samples : uint8_t(1); diff --git a/filament/src/fg/PassNode.cpp b/filament/src/fg/PassNode.cpp index 605d3633e5c..b6b7c7a906b 100644 --- a/filament/src/fg/PassNode.cpp +++ b/filament/src/fg/PassNode.cpp @@ -190,6 +190,8 @@ void RenderPassNode::resolve() noexcept { rt.descriptor.clearFlags & rt.targetBufferFlags); } + assert_invariant(minWidth == maxWidth); + assert_invariant(minHeight == maxHeight); assert_invariant(any(rt.targetBufferFlags)); // of all attachments size matches there are no ambiguity about the RT size.