core: refactor renderable size ownership (#4512)

This commit is contained in:
Bart Louwers
2026-09-01 17:35:29 +02:00
committed by GitHub
parent be3f03b86e
commit e5d0fce32a
35 changed files with 131 additions and 122 deletions

View File

@@ -34,6 +34,7 @@ protected:
virtual ~Renderable() = default; virtual ~Renderable() = default;
public: public:
/// Returns the renderable dimensions in physical pixels.
Size getSize() const { return size; } Size getSize() const { return size; }
template <typename T> template <typename T>
@@ -50,8 +51,12 @@ public:
bool operator!=(const Renderable& other) const { return resource.get() != other.resource.get(); } bool operator!=(const Renderable& other) const { return resource.get() != other.resource.get(); }
protected: private:
Size size; Size size;
protected:
void setRenderableSize(const Size& size_) noexcept { size = size_; }
std::unique_ptr<RenderableResource> resource; std::unique_ptr<RenderableResource> resource;
}; };

View File

@@ -116,7 +116,7 @@ protected:
~Renderable() override = default; ~Renderable() override = default;
public: public:
void setSize(const Size& size_) { size = size_; } void setSize(const Size& size_) { setRenderableSize(size_); }
}; };
} // namespace vulkan } // namespace vulkan

View File

@@ -46,21 +46,21 @@ gl::ProcAddress AndroidGLRendererBackend::getExtensionFunctionPointer(const char
void AndroidGLRendererBackend::updateViewPort() { void AndroidGLRendererBackend::updateViewPort() {
assert(gfx::BackendScope::exists()); assert(gfx::BackendScope::exists());
setViewport(0, 0, size); setViewport(0, 0, getSize());
} }
void AndroidGLRendererBackend::resizeFramebuffer(int width, int height) { void AndroidGLRendererBackend::resizeFramebuffer(int width, int height) {
size = {static_cast<uint32_t>(width), static_cast<uint32_t>(height)}; setRenderableSize({static_cast<uint32_t>(width), static_cast<uint32_t>(height)});
} }
PremultipliedImage AndroidGLRendererBackend::readFramebuffer() { PremultipliedImage AndroidGLRendererBackend::readFramebuffer() {
assert(gfx::BackendScope::exists()); assert(gfx::BackendScope::exists());
return gl::RendererBackend::readFramebuffer(size); return gl::RendererBackend::readFramebuffer(getSize());
} }
void AndroidGLRendererBackend::updateAssumedState() { void AndroidGLRendererBackend::updateAssumedState() {
assumeFramebufferBinding(0); assumeFramebufferBinding(0);
assumeViewport(0, 0, size); assumeViewport(0, 0, getSize());
} }
void AndroidGLRendererBackend::markContextLost() { void AndroidGLRendererBackend::markContextLost() {

View File

@@ -160,8 +160,6 @@ public:
WGPUTexture currentTexture = nullptr; WGPUTexture currentTexture = nullptr;
WGPUTextureView currentTextureView = nullptr; WGPUTextureView currentTextureView = nullptr;
bool needsPresent = false; bool needsPresent = false;
Size framebufferSize;
}; };
AndroidWebGPURendererBackend::AndroidWebGPURendererBackend(ANativeWindow* window_) AndroidWebGPURendererBackend::AndroidWebGPURendererBackend(ANativeWindow* window_)
@@ -303,9 +301,8 @@ AndroidWebGPURendererBackend::AndroidWebGPURendererBackend(ANativeWindow* window
const int w = ANativeWindow_getWidth(window); const int w = ANativeWindow_getWidth(window);
const int h = ANativeWindow_getHeight(window); const int h = ANativeWindow_getHeight(window);
if (w > 0 && h > 0) { if (w > 0 && h > 0) {
impl->framebufferSize = {static_cast<uint32_t>(w), static_cast<uint32_t>(h)}; setRenderableSize({static_cast<uint32_t>(w), static_cast<uint32_t>(h)});
size = impl->framebufferSize; configureSurface(static_cast<uint32_t>(w), static_cast<uint32_t>(h));
configureSurface(impl->framebufferSize.width, impl->framebufferSize.height);
} }
} }
@@ -508,8 +505,7 @@ void AndroidWebGPURendererBackend::createDepthStencilTexture(uint32_t width, uin
void AndroidWebGPURendererBackend::resizeFramebuffer(int width, int height) { void AndroidWebGPURendererBackend::resizeFramebuffer(int width, int height) {
if (width <= 0 || height <= 0) return; if (width <= 0 || height <= 0) return;
impl->framebufferSize = {static_cast<uint32_t>(width), static_cast<uint32_t>(height)}; setRenderableSize({static_cast<uint32_t>(width), static_cast<uint32_t>(height)});
size = impl->framebufferSize;
if (impl->currentTextureView) { if (impl->currentTextureView) {
wgpuTextureViewRelease(impl->currentTextureView); wgpuTextureViewRelease(impl->currentTextureView);
@@ -520,16 +516,16 @@ void AndroidWebGPURendererBackend::resizeFramebuffer(int width, int height) {
impl->currentTexture = nullptr; impl->currentTexture = nullptr;
} }
configureSurface(impl->framebufferSize.width, impl->framebufferSize.height); configureSurface(static_cast<uint32_t>(width), static_cast<uint32_t>(height));
} }
PremultipliedImage AndroidWebGPURendererBackend::readFramebuffer() { PremultipliedImage AndroidWebGPURendererBackend::readFramebuffer() {
if (!impl->currentTexture || !impl->device || !impl->queue || impl->framebufferSize.width == 0 || const auto framebufferSize = getSize();
impl->framebufferSize.height == 0) { if (!impl->currentTexture || !impl->device || !impl->queue || framebufferSize.isEmpty()) {
return PremultipliedImage(impl->framebufferSize.isEmpty() ? Size(2, 2) : impl->framebufferSize); return PremultipliedImage(framebufferSize.isEmpty() ? Size(2, 2) : framebufferSize);
} }
const auto& fbSize = impl->framebufferSize; const auto& fbSize = framebufferSize;
constexpr uint32_t bytesPerPixel = 4; constexpr uint32_t bytesPerPixel = 4;
constexpr uint32_t bytesPerRowAlignment = 256u; constexpr uint32_t bytesPerRowAlignment = 256u;
const uint32_t rowStride = fbSize.width * bytesPerPixel; const uint32_t rowStride = fbSize.width * bytesPerPixel;
@@ -683,7 +679,8 @@ void* AndroidWebGPURendererBackend::getCurrentTextureView() {
auto status = static_cast<WGPUSurfaceGetCurrentTextureStatus>(surfaceTexture.status); auto status = static_cast<WGPUSurfaceGetCurrentTextureStatus>(surfaceTexture.status);
if (status == WGPUSurfaceGetCurrentTextureStatus_Outdated || status == WGPUSurfaceGetCurrentTextureStatus_Lost) { if (status == WGPUSurfaceGetCurrentTextureStatus_Outdated || status == WGPUSurfaceGetCurrentTextureStatus_Lost) {
wgpuTextureRelease(surfaceTexture.texture); wgpuTextureRelease(surfaceTexture.texture);
configureSurface(impl->framebufferSize.width, impl->framebufferSize.height); const auto framebufferSize = getSize();
configureSurface(framebufferSize.width, framebufferSize.height);
return nullptr; return nullptr;
} }
@@ -698,10 +695,9 @@ void* AndroidWebGPURendererBackend::getCurrentTextureView() {
// Actual texture size may differ from configured size (Vulkan currentExtent clamping) // Actual texture size may differ from configured size (Vulkan currentExtent clamping)
const uint32_t texW = wgpuTextureGetWidth(surfaceTexture.texture); const uint32_t texW = wgpuTextureGetWidth(surfaceTexture.texture);
const uint32_t texH = wgpuTextureGetHeight(surfaceTexture.texture); const uint32_t texH = wgpuTextureGetHeight(surfaceTexture.texture);
if (texW != size.width || texH != size.height) { if (const auto framebufferSize = getSize(); texW != framebufferSize.width || texH != framebufferSize.height) {
if (texW > 0 && texH > 0) { if (texW > 0 && texH > 0) {
size = {texW, texH}; setRenderableSize({texW, texH});
impl->framebufferSize = size;
createDepthStencilTexture(texW, texH); createDepthStencilTexture(texW, texH);
} }
} }
@@ -759,7 +755,7 @@ void* AndroidWebGPURendererBackend::getDepthStencilView() {
} }
mln::Size AndroidWebGPURendererBackend::getFramebufferSize() const { mln::Size AndroidWebGPURendererBackend::getFramebufferSize() const {
return impl->framebufferSize; return getSize();
} }
} // namespace android } // namespace android

View File

@@ -9,7 +9,7 @@ HeadlessBackend::HeadlessBackend(Size size_)
: mln::gfx::Renderable(size_, nullptr) {} : mln::gfx::Renderable(size_, nullptr) {}
void HeadlessBackend::setSize(Size size_) { void HeadlessBackend::setSize(Size size_) {
size = size_; setRenderableSize(size_);
resource.reset(); resource.reset();
} }

View File

@@ -97,7 +97,8 @@ gfx::Renderable& HeadlessBackend::getDefaultRenderable() {
MLN_TRACE_FUNC(); MLN_TRACE_FUNC();
if (!resource) { if (!resource) {
resource = std::make_unique<HeadlessRenderableResource>(*this, static_cast<gl::Context&>(getContext()), size); resource = std::make_unique<HeadlessRenderableResource>(
*this, static_cast<gl::Context&>(getContext()), getSize());
} }
return *this; return *this;
} }
@@ -115,7 +116,7 @@ void HeadlessBackend::updateAssumedState() {
PremultipliedImage HeadlessBackend::readStillImage() { PremultipliedImage HeadlessBackend::readStillImage() {
MLN_TRACE_FUNC(); MLN_TRACE_FUNC();
return static_cast<gl::Context&>(getContext()).readFramebuffer<PremultipliedImage>(size); return static_cast<gl::Context&>(getContext()).readFramebuffer<PremultipliedImage>(getSize());
} }
RendererBackend* HeadlessBackend::getRendererBackend() { RendererBackend* HeadlessBackend::getRendererBackend() {

View File

@@ -83,7 +83,8 @@ gfx::Renderable& HeadlessBackend::getDefaultRenderable() {
void HeadlessBackend::ensureResource() { void HeadlessBackend::ensureResource() {
if (!resource) { if (!resource) {
resource = std::make_unique<HeadlessRenderableResource>(*this, static_cast<mtl::Context&>(getContext()), size); resource = std::make_unique<HeadlessRenderableResource>(
*this, static_cast<mtl::Context&>(getContext()), getSize());
} }
} }

View File

@@ -23,6 +23,6 @@ public:
} }
virtual mln::gfx::RendererBackend& getRendererBackend() = 0; virtual mln::gfx::RendererBackend& getRendererBackend() = 0;
virtual mln::Size getSize() const = 0; virtual mln::Size getFramebufferSize() const = 0;
virtual void setSize(mln::Size) = 0; virtual void setFramebufferSize(mln::Size) = 0;
}; };

View File

@@ -15,7 +15,7 @@ public:
MLN_TRACE_FUNC(); MLN_TRACE_FUNC();
backend.setFramebufferBinding(0); backend.setFramebufferBinding(0);
backend.setViewport(0, 0, backend.getSize()); backend.setViewport(0, 0, backend.getFramebufferSize());
} }
void swap() override { void swap() override {
@@ -72,15 +72,15 @@ void GLFWGLBackend::updateAssumedState() {
MLN_TRACE_FUNC(); MLN_TRACE_FUNC();
assumeFramebufferBinding(0); assumeFramebufferBinding(0);
setViewport(0, 0, size); setViewport(0, 0, getSize());
} }
mln::Size GLFWGLBackend::getSize() const { mln::Size GLFWGLBackend::getFramebufferSize() const {
return size; return getSize();
} }
void GLFWGLBackend::setSize(const mln::Size newSize) { void GLFWGLBackend::setFramebufferSize(const mln::Size newSize) {
size = newSize; setRenderableSize(newSize);
} }
void GLFWGLBackend::swap() { void GLFWGLBackend::swap() {

View File

@@ -17,8 +17,8 @@ public:
// GLFWRendererBackend implementation // GLFWRendererBackend implementation
public: public:
mln::gfx::RendererBackend& getRendererBackend() override { return *this; } mln::gfx::RendererBackend& getRendererBackend() override { return *this; }
mln::Size getSize() const override; mln::Size getFramebufferSize() const override;
void setSize(mln::Size) override; void setFramebufferSize(mln::Size) override;
// mln::gfx::RendererBackend implementation // mln::gfx::RendererBackend implementation
public: public:

View File

@@ -14,8 +14,8 @@ public:
GLFWMetalBackend(GLFWwindow *window_, const bool capFrameRate); GLFWMetalBackend(GLFWwindow *window_, const bool capFrameRate);
~GLFWMetalBackend() = default; ~GLFWMetalBackend() = default;
mln::gfx::RendererBackend &getRendererBackend() override; mln::gfx::RendererBackend &getRendererBackend() override;
void setSize(mln::Size) override; void setFramebufferSize(mln::Size) override;
mln::Size getSize() const override; mln::Size getFramebufferSize() const override;
private: private:
NSWindow *window; NSWindow *window;

View File

@@ -11,6 +11,6 @@ GLFWMetalBackend::GLFWMetalBackend(GLFWwindow* window_, [[maybe_unused]] const b
mln::gfx::RendererBackend& GLFWMetalBackend::getRendererBackend() { return rendererBackend; } mln::gfx::RendererBackend& GLFWMetalBackend::getRendererBackend() { return rendererBackend; }
void GLFWMetalBackend::setSize(mln::Size size) { rendererBackend.setSize(size); } void GLFWMetalBackend::setFramebufferSize(mln::Size size) { rendererBackend.setSize(size); }
mln::Size GLFWMetalBackend::getSize() const { return rendererBackend.getSize(); } mln::Size GLFWMetalBackend::getFramebufferSize() const { return rendererBackend.getSize(); }

View File

@@ -312,10 +312,10 @@ GLFWView::GLFWView(bool fullscreen_,
#if defined(__APPLE__) && !defined(MLN_RENDER_BACKEND_VULKAN) #if defined(__APPLE__) && !defined(MLN_RENDER_BACKEND_VULKAN)
int fbW, fbH; int fbW, fbH;
glfwGetFramebufferSize(window, &fbW, &fbH); glfwGetFramebufferSize(window, &fbW, &fbH);
backend->setSize({static_cast<uint32_t>(fbW), static_cast<uint32_t>(fbH)}); backend->setFramebufferSize({static_cast<uint32_t>(fbW), static_cast<uint32_t>(fbH)});
#endif #endif
pixelRatio = static_cast<float>(backend->getSize().width) / width; pixelRatio = static_cast<float>(backend->getFramebufferSize().width) / width;
glfwMakeContextCurrent(nullptr); glfwMakeContextCurrent(nullptr);
@@ -1026,7 +1026,7 @@ void GLFWView::onWindowResize(GLFWwindow *window, int width, int height) {
#ifdef __APPLE__ #ifdef __APPLE__
int fbW, fbH; int fbW, fbH;
glfwGetFramebufferSize(window, &fbW, &fbH); glfwGetFramebufferSize(window, &fbW, &fbH);
view->backend->setSize({static_cast<uint32_t>(fbW), static_cast<uint32_t>(fbH)}); view->backend->setFramebufferSize({static_cast<uint32_t>(fbW), static_cast<uint32_t>(fbH)});
#endif #endif
} }
@@ -1034,7 +1034,7 @@ void GLFWView::onFramebufferResize(GLFWwindow *window, int width, int height) {
MLN_TRACE_FUNC(); MLN_TRACE_FUNC();
auto *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window)); auto *view = reinterpret_cast<GLFWView *>(glfwGetWindowUserPointer(window));
view->backend->setSize({static_cast<uint32_t>(width), static_cast<uint32_t>(height)}); view->backend->setFramebufferSize({static_cast<uint32_t>(width), static_cast<uint32_t>(height)});
// This is only triggered when the framebuffer is resized, but not the // This is only triggered when the framebuffer is resized, but not the
// window. It can happen when you move the window between screens with a // window. It can happen when you move the window between screens with a

View File

@@ -156,12 +156,12 @@ GLFWVulkanBackend::GLFWVulkanBackend(GLFWwindow* window_, const bool capFrameRat
GLFWVulkanBackend::~GLFWVulkanBackend() {} GLFWVulkanBackend::~GLFWVulkanBackend() {}
mln::Size GLFWVulkanBackend::getSize() const { mln::Size GLFWVulkanBackend::getFramebufferSize() const {
return size; return getSize();
} }
void GLFWVulkanBackend::setSize(const mln::Size newSize) { void GLFWVulkanBackend::setFramebufferSize(const mln::Size newSize) {
size = newSize; setRenderableSize(newSize);
auto& contextImpl = static_cast<mln::vulkan::Context&>(*context); auto& contextImpl = static_cast<mln::vulkan::Context&>(*context);
contextImpl.requestSurfaceUpdate(); contextImpl.requestSurfaceUpdate();

View File

@@ -23,8 +23,8 @@ public:
// GLFWRendererBackend implementation // GLFWRendererBackend implementation
public: public:
mln::gfx::RendererBackend& getRendererBackend() override { return *this; } mln::gfx::RendererBackend& getRendererBackend() override { return *this; }
mln::Size getSize() const override; mln::Size getFramebufferSize() const override;
void setSize(mln::Size) override; void setFramebufferSize(mln::Size) override;
// mln::gfx::RendererBackend implementation // mln::gfx::RendererBackend implementation
public: public:

View File

@@ -34,8 +34,8 @@ public:
// GLFWBackend implementation // GLFWBackend implementation
public: public:
mln::gfx::RendererBackend& getRendererBackend() override; mln::gfx::RendererBackend& getRendererBackend() override;
mln::Size getSize() const override; mln::Size getFramebufferSize() const override;
void setSize(mln::Size) override; void setFramebufferSize(mln::Size) override;
// mln::gfx::RendererBackend implementation // mln::gfx::RendererBackend implementation
public: public:
@@ -50,7 +50,6 @@ public:
// Override virtual methods from RendererBackend // Override virtual methods from RendererBackend
void* getCurrentTextureView() override; void* getCurrentTextureView() override;
void* getDepthStencilView() override; void* getDepthStencilView() override;
mln::Size getFramebufferSize() const override;
private: private:
class SpinLock { class SpinLock {

View File

@@ -896,13 +896,13 @@ void GLFWWebGPUBackend::deactivate() {
// WebGPU doesn't need explicit context deactivation like OpenGL // WebGPU doesn't need explicit context deactivation like OpenGL
} }
mln::Size GLFWWebGPUBackend::getSize() const { mln::Size GLFWWebGPUBackend::getFramebufferSize() const {
return size; return getSize();
} }
void GLFWWebGPUBackend::setSize(mln::Size newSize) { void GLFWWebGPUBackend::setFramebufferSize(mln::Size newSize) {
// Update swap chain size if needed // Update swap chain size if needed
if (size != newSize) { if (getSize() != newSize) {
#ifdef __APPLE__ #ifdef __APPLE__
int width = 0; int width = 0;
int height = 0; int height = 0;
@@ -918,7 +918,7 @@ void GLFWWebGPUBackend::setSize(mln::Size newSize) {
} }
} }
#endif #endif
size = newSize; setRenderableSize(newSize);
surfaceNeedsReconfigure = true; surfaceNeedsReconfigure = true;
} }
} }
@@ -1066,10 +1066,6 @@ void* GLFWWebGPUBackend::getCurrentTextureView() {
#endif #endif
} }
mln::Size GLFWWebGPUBackend::getFramebufferSize() const {
return getSize();
}
void* GLFWWebGPUBackend::getDepthStencilView() { void* GLFWWebGPUBackend::getDepthStencilView() {
std::lock_guard<SpinLock> guard(textureStateLock); std::lock_guard<SpinLock> guard(textureStateLock);
if (!depthStencilView) { if (!depthStencilView) {
@@ -1126,7 +1122,8 @@ void GLFWWebGPUBackend::reconfigureSurface() {
return; return;
} }
size = {static_cast<uint32_t>(std::max(width, 0)), static_cast<uint32_t>(std::max(height, 0))}; setRenderableSize(
{static_cast<uint32_t>(std::max(width, 0)), static_cast<uint32_t>(std::max(height, 0))});
// Configure surface // Configure surface
wgpu::SurfaceConfiguration config = {}; wgpu::SurfaceConfiguration config = {};

View File

@@ -15,5 +15,4 @@ public:
void activate() override; void activate() override;
void deactivate() override; void deactivate() override;
void setSize(mln::Size size_); void setSize(mln::Size size_);
mln::Size getSize() const;
}; };

View File

@@ -19,15 +19,12 @@ public:
swapchain->setDevice(backend.getDevice().get()); swapchain->setDevice(backend.getDevice().get());
} }
void setBackendSize(mln::Size size_) { void setDrawableSize(const mln::Size& drawableSize) {
size = size_;
swapchain->setDrawableSize( swapchain->setDrawableSize(
{static_cast<CGFloat>(size.width), static_cast<CGFloat>(size.height)}); {static_cast<CGFloat>(drawableSize.width), static_cast<CGFloat>(drawableSize.height)});
buffersInvalid = true; buffersInvalid = true;
} }
mln::Size getSize() const { return size; }
void bind() override { void bind() override {
surface = NS::TransferPtr(swapchain->nextDrawable()); surface = NS::TransferPtr(swapchain->nextDrawable());
auto texSize = mln::Size{static_cast<uint32_t>(swapchain->drawableSize().width), auto texSize = mln::Size{static_cast<uint32_t>(swapchain->drawableSize().width),
@@ -107,7 +104,6 @@ private:
CAMetalLayerPtr swapchain; CAMetalLayerPtr swapchain;
gfx::Texture2DPtr depthTexture; gfx::Texture2DPtr depthTexture;
gfx::Texture2DPtr stencilTexture; gfx::Texture2DPtr stencilTexture;
mln::Size size;
bool buffersInvalid = true; bool buffersInvalid = true;
}; };
@@ -129,10 +125,6 @@ void MetalBackend::activate() {}
void MetalBackend::deactivate() {} void MetalBackend::deactivate() {}
void MetalBackend::setSize(mln::Size size_) { void MetalBackend::setSize(mln::Size size_) {
size = size_; setRenderableSize(size_);
getResource<mln::MetalRenderableResource>().setBackendSize(size_); getResource<mln::MetalRenderableResource>().setDrawableSize(size_);
}
mln::Size MetalBackend::getSize() const {
return getResource<mln::MetalRenderableResource>().getSize();
} }

View File

@@ -235,7 +235,7 @@ void MLNMapViewMetalImpl::layoutChanged() {
resource.mtlView.drawableSize = resource.lastAppliedDrawableSize = target; resource.mtlView.drawableSize = resource.lastAppliedDrawableSize = target;
} }
size = {static_cast<uint32_t>(target.width), static_cast<uint32_t>(target.height)}; setRenderableSize({static_cast<uint32_t>(target.width), static_cast<uint32_t>(target.height)});
} }
MLNBackendResource* MLNMapViewMetalImpl::getObject() { MLNBackendResource* MLNMapViewMetalImpl::getObject() {

View File

@@ -241,8 +241,8 @@ UIImage* MLNMapViewOpenGLImpl::snapshot() {
void MLNMapViewOpenGLImpl::layoutChanged() { void MLNMapViewOpenGLImpl::layoutChanged() {
const auto scaleFactor = contentScaleFactor(); const auto scaleFactor = contentScaleFactor();
size = {static_cast<uint32_t>(mapView.bounds.size.width * scaleFactor), setRenderableSize({static_cast<uint32_t>(mapView.bounds.size.width * scaleFactor),
static_cast<uint32_t>(mapView.bounds.size.height * scaleFactor)}; static_cast<uint32_t>(mapView.bounds.size.height * scaleFactor)});
} }
EAGLContext* MLNMapViewOpenGLImpl::getEAGLContext() { EAGLContext* MLNMapViewOpenGLImpl::getEAGLContext() {

View File

@@ -94,8 +94,6 @@ public:
WGPUTextureView currentTextureView = nullptr; WGPUTextureView currentTextureView = nullptr;
bool needsPresent = false; bool needsPresent = false;
mln::Size framebufferSize;
MLNWebGPUView* webGPUView = nil; MLNWebGPUView* webGPUView = nil;
bool presentsWithTransaction = false; bool presentsWithTransaction = false;
NSUInteger activationCount = 0; NSUInteger activationCount = 0;
@@ -316,8 +314,7 @@ void MLNMapViewWebGPUImpl::createView() {
uint32_t w = static_cast<uint32_t>(drawableSize.width); uint32_t w = static_cast<uint32_t>(drawableSize.width);
uint32_t h = static_cast<uint32_t>(drawableSize.height); uint32_t h = static_cast<uint32_t>(drawableSize.height);
if (w > 0 && h > 0) { if (w > 0 && h > 0) {
impl->framebufferSize = {w, h}; setRenderableSize({w, h});
size = impl->framebufferSize;
configureSurface(w, h); configureSurface(w, h);
} }
} }
@@ -497,7 +494,8 @@ void* MLNMapViewWebGPUImpl::getCurrentTextureView() {
if (status == WGPUSurfaceGetCurrentTextureStatus_Outdated || if (status == WGPUSurfaceGetCurrentTextureStatus_Outdated ||
status == WGPUSurfaceGetCurrentTextureStatus_Lost) { status == WGPUSurfaceGetCurrentTextureStatus_Lost) {
wgpuTextureRelease(surfaceTexture.texture); wgpuTextureRelease(surfaceTexture.texture);
configureSurface(impl->framebufferSize.width, impl->framebufferSize.height); const auto framebufferSize = getSize();
configureSurface(framebufferSize.width, framebufferSize.height);
return nullptr; return nullptr;
} }
@@ -557,7 +555,7 @@ void MLNMapViewWebGPUImpl::presentSurface() {
void* MLNMapViewWebGPUImpl::getDepthStencilView() { return impl->depthStencilView; } void* MLNMapViewWebGPUImpl::getDepthStencilView() { return impl->depthStencilView; }
mln::Size MLNMapViewWebGPUImpl::getFramebufferSize() const { return impl->framebufferSize; } mln::Size MLNMapViewWebGPUImpl::getFramebufferSize() const { return getSize(); }
UIView* MLNMapViewWebGPUImpl::getView() { return impl->webGPUView; } UIView* MLNMapViewWebGPUImpl::getView() { return impl->webGPUView; }
@@ -624,8 +622,7 @@ void MLNMapViewWebGPUImpl::layoutChanged() {
uint32_t w = static_cast<uint32_t>(drawableSize.width); uint32_t w = static_cast<uint32_t>(drawableSize.width);
uint32_t h = static_cast<uint32_t>(drawableSize.height); uint32_t h = static_cast<uint32_t>(drawableSize.height);
impl->framebufferSize = {w, h}; setRenderableSize({w, h});
size = impl->framebufferSize;
if (impl->currentTextureView) { if (impl->currentTextureView) {
wgpuTextureViewRelease(impl->currentTextureView); wgpuTextureViewRelease(impl->currentTextureView);
@@ -642,12 +639,12 @@ void MLNMapViewWebGPUImpl::layoutChanged() {
} }
UIImage* MLNMapViewWebGPUImpl::snapshot() { UIImage* MLNMapViewWebGPUImpl::snapshot() {
if (!impl->currentTexture || !impl->device || !impl->queue || impl->framebufferSize.width == 0 || const auto framebufferSize = getSize();
impl->framebufferSize.height == 0) { if (!impl->currentTexture || !impl->device || !impl->queue || framebufferSize.isEmpty()) {
return nil; return nil;
} }
const auto& fbSize = impl->framebufferSize; const auto& fbSize = framebufferSize;
constexpr uint32_t bytesPerPixel = 4; constexpr uint32_t bytesPerPixel = 4;
constexpr uint32_t bytesPerRowAlignment = 256u; constexpr uint32_t bytesPerRowAlignment = 256u;
const uint32_t rowStride = fbSize.width * bytesPerPixel; const uint32_t rowStride = fbSize.width * bytesPerPixel;

View File

@@ -139,7 +139,8 @@ MLNMapViewMetalImpl::MLNMapViewMetalImpl(MLNMapView* nativeView_)
} }
void MLNMapViewMetalImpl::drawableSizeChanged(CGSize drawableSize) { void MLNMapViewMetalImpl::drawableSizeChanged(CGSize drawableSize) {
size = {static_cast<uint32_t>(drawableSize.width), static_cast<uint32_t>(drawableSize.height)}; setRenderableSize(
{static_cast<uint32_t>(drawableSize.width), static_cast<uint32_t>(drawableSize.height)});
} }
MLNMapViewMetalImpl::~MLNMapViewMetalImpl() = default; MLNMapViewMetalImpl::~MLNMapViewMetalImpl() = default;

View File

@@ -57,7 +57,7 @@ private:
}; };
OffscreenTexture::OffscreenTexture(gl::Context& context, const Size size_, const gfx::TextureChannelDataType type) OffscreenTexture::OffscreenTexture(gl::Context& context, const Size size_, const gfx::TextureChannelDataType type)
: gfx::OffscreenTexture(size, std::make_unique<OffscreenTextureResource>(context, size_, type)) {} : gfx::OffscreenTexture(size_, std::make_unique<OffscreenTextureResource>(context, size_, type)) {}
bool OffscreenTexture::isRenderable() { bool OffscreenTexture::isRenderable() {
try { try {

View File

@@ -134,7 +134,7 @@ private:
OffscreenTexture::OffscreenTexture( OffscreenTexture::OffscreenTexture(
Context& context, const Size size_, const gfx::TextureChannelDataType type, bool depth, bool stencil) Context& context, const Size size_, const gfx::TextureChannelDataType type, bool depth, bool stencil)
: gfx::OffscreenTexture(size, std::make_unique<OffscreenTextureResource>(context, size_, type, depth, stencil)) {} : gfx::OffscreenTexture(size_, std::make_unique<OffscreenTextureResource>(context, size_, type, depth, stencil)) {}
bool OffscreenTexture::isRenderable() { bool OffscreenTexture::isRenderable() {
assert(false); assert(false);

View File

@@ -4,7 +4,6 @@
#include <mln/gfx/drawable.hpp> #include <mln/gfx/drawable.hpp>
#include <mln/renderer/layer_group.hpp> #include <mln/renderer/layer_group.hpp>
#include <mln/renderer/paint_parameters.hpp> #include <mln/renderer/paint_parameters.hpp>
#include <mln/renderer/render_static_data.hpp>
#include <mln/renderer/render_tree.hpp> #include <mln/renderer/render_tree.hpp>
#include <mln/shaders/heatmap_texture_layer_ubo.hpp> #include <mln/shaders/heatmap_texture_layer_ubo.hpp>
#include <mln/style/layers/heatmap_layer_properties.hpp> #include <mln/style/layers/heatmap_layer_properties.hpp>
@@ -30,7 +29,7 @@ void HeatmapTextureLayerTweaker::execute(LayerGroupBase& layerGroup, const Paint
propertiesUpdated = false; propertiesUpdated = false;
mat4 matrix; mat4 matrix;
const auto& size = parameters.staticData.backendSize; const auto& size = parameters.renderableSize;
matrix::ortho(matrix, 0, size.width, size.height, 0, -1, 1); matrix::ortho(matrix, 0, size.width, size.height, 0, -1, 1);
const HeatmapTexturePropsUBO propsUBO = {.matrix = util::cast<float>(matrix), const HeatmapTexturePropsUBO propsUBO = {.matrix = util::cast<float>(matrix),

View File

@@ -66,6 +66,7 @@ PaintParameters::PaintParameters(gfx::Context& context_,
double tileLodScale_, double tileLodScale_,
double tileLodPitchThreshold_, double tileLodPitchThreshold_,
TileLodMode tileLodMode_, TileLodMode tileLodMode_,
Size renderableSize_,
const gfx::ScissorRect& scissorRect_) const gfx::ScissorRect& scissorRect_)
: context(context_), : context(context_),
backend(backend_), backend(backend_),
@@ -80,6 +81,7 @@ PaintParameters::PaintParameters(gfx::Context& context_,
debugOptions(debugOptions_), debugOptions(debugOptions_),
timePoint(timePoint_), timePoint(timePoint_),
pixelRatio(pixelRatio_), pixelRatio(pixelRatio_),
renderableSize(renderableSize_),
shaders(*staticData_.shaders), shaders(*staticData_.shaders),
frameCount(frameCount_), frameCount(frameCount_),
tileLodMinRadius(tileLodMinRadius_), tileLodMinRadius(tileLodMinRadius_),

View File

@@ -63,6 +63,7 @@ public:
double tileLodScale, double tileLodScale,
double tileLodPitchThreshold, double tileLodPitchThreshold,
TileLodMode tileLodMode, TileLodMode tileLodMode,
Size renderableSize,
const gfx::ScissorRect&); const gfx::ScissorRect&);
~PaintParameters(); ~PaintParameters();
@@ -85,6 +86,7 @@ public:
TimePoint timePoint; TimePoint timePoint;
float pixelRatio; float pixelRatio;
const Size renderableSize;
std::array<float, 2> pixelsToGLUnits; std::array<float, 2> pixelsToGLUnits;
gfx::ShaderRegistry& shaders; gfx::ShaderRegistry& shaders;

View File

@@ -48,7 +48,6 @@ public:
std::optional<gfx::Renderbuffer<gfx::RenderbufferPixelType::Depth>> depthRenderbuffer; std::optional<gfx::Renderbuffer<gfx::RenderbufferPixelType::Depth>> depthRenderbuffer;
bool has3D = false; bool has3D = false;
bool uploaded = false; bool uploaded = false;
Size backendSize;
std::unique_ptr<gfx::ShaderRegistry> shaders; std::unique_ptr<gfx::ShaderRegistry> shaders;

View File

@@ -181,7 +181,7 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr<
const auto& renderTreeParameters = renderTree.getParameters(); const auto& renderTreeParameters = renderTree.getParameters();
staticData->has3D = renderTreeParameters.has3D; staticData->has3D = renderTreeParameters.has3D;
staticData->backendSize = backend.getDefaultRenderable().getSize(); const Size renderableSize = backend.getDefaultRenderable().getSize();
if (renderState == RenderState::Never) { if (renderState == RenderState::Never) {
observer->onWillStartRenderingMap(); observer->onWillStartRenderingMap();
@@ -190,7 +190,6 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr<
observer->onWillStartRenderingFrame(); observer->onWillStartRenderingFrame();
const TransformState& state = renderTreeParameters.transformParams.state; const TransformState& state = renderTreeParameters.transformParams.state;
const Size& size = staticData->backendSize;
const EdgeInsets& frustumOffset = state.getFrustumOffset(); const EdgeInsets& frustumOffset = state.getFrustumOffset();
const gfx::ScissorRect scissorRect = { const gfx::ScissorRect scissorRect = {
.x = static_cast<int32_t>(frustumOffset.left() * pixelRatio), .x = static_cast<int32_t>(frustumOffset.left() * pixelRatio),
@@ -199,8 +198,10 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr<
#else #else
.y = static_cast<int32_t>(frustumOffset.top() * pixelRatio), .y = static_cast<int32_t>(frustumOffset.top() * pixelRatio),
#endif #endif
.width = size.width - static_cast<uint32_t>((frustumOffset.left() + frustumOffset.right()) * pixelRatio), .width = renderableSize.width -
.height = size.height - static_cast<uint32_t>((frustumOffset.top() + frustumOffset.bottom()) * pixelRatio), static_cast<uint32_t>((frustumOffset.left() + frustumOffset.right()) * pixelRatio),
.height = renderableSize.height -
static_cast<uint32_t>((frustumOffset.top() + frustumOffset.bottom()) * pixelRatio),
}; };
PaintParameters parameters{ PaintParameters parameters{
@@ -220,6 +221,7 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr<
updateParameters->tileLodScale, updateParameters->tileLodScale,
updateParameters->tileLodPitchThreshold, updateParameters->tileLodPitchThreshold,
updateParameters->tileLodMode, updateParameters->tileLodMode,
renderableSize,
scissorRect, scissorRect,
}; };
@@ -297,7 +299,7 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr<
} }
const Size atlasSize = parameters.patternAtlas.getPixelSize(); const Size atlasSize = parameters.patternAtlas.getPixelSize();
const auto& worldSize = parameters.staticData.backendSize; const auto& worldSize = parameters.renderableSize;
const shaders::GlobalPaintParamsUBO globalPaintParamsUBO = { const shaders::GlobalPaintParamsUBO globalPaintParamsUBO = {
.pattern_atlas_texsize = {static_cast<float>(atlasSize.width), static_cast<float>(atlasSize.height)}, .pattern_atlas_texsize = {static_cast<float>(atlasSize.width), static_cast<float>(atlasSize.height)},
.units_to_pixels = {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]}, .units_to_pixels = {1.0f / parameters.pixelsToGLUnits[0], 1.0f / parameters.pixelsToGLUnits[1]},
@@ -318,8 +320,6 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr<
// attachments, but share the same depth rbo between them. // attachments, but share the same depth rbo between them.
const auto common3DPass = [&] { const auto common3DPass = [&] {
if (parameters.staticData.has3D) { if (parameters.staticData.has3D) {
parameters.staticData.backendSize = parameters.backend.getDefaultRenderable().getSize();
const auto debugGroup(parameters.encoder->createDebugGroup("common-3d")); const auto debugGroup(parameters.encoder->createDebugGroup("common-3d"));
parameters.pass = RenderPass::Pass3D; parameters.pass = RenderPass::Pass3D;
#if MLN_RENDER_BACKEND_OPENGL #if MLN_RENDER_BACKEND_OPENGL
@@ -328,10 +328,10 @@ void Renderer::Impl::render(const RenderTree& renderTree, const std::shared_ptr<
// TODO is this needed? // TODO is this needed?
// if (!parameters.staticData.depthRenderbuffer || // if (!parameters.staticData.depthRenderbuffer ||
// parameters.staticData.depthRenderbuffer->getSize() != parameters.staticData.backendSize) { // parameters.staticData.depthRenderbuffer->getSize() != parameters.renderableSize) {
// parameters.staticData.depthRenderbuffer = // parameters.staticData.depthRenderbuffer =
// parameters.context.createRenderbuffer<gfx::RenderbufferPixelType::Depth>( // parameters.context.createRenderbuffer<gfx::RenderbufferPixelType::Depth>(
// parameters.staticData.backendSize); // parameters.renderableSize);
//} //}
// parameters.staticData.depthRenderbuffer->setShouldClear(true); // parameters.staticData.depthRenderbuffer->setShouldClear(true);
} }

View File

@@ -133,7 +133,7 @@ OffscreenTexture::OffscreenTexture(Context& context,
const gfx::TextureChannelDataType type, const gfx::TextureChannelDataType type,
[[maybe_unused]] bool depth, [[maybe_unused]] bool depth,
[[maybe_unused]] bool stencil) [[maybe_unused]] bool stencil)
: gfx::OffscreenTexture(size, std::make_unique<OffscreenTextureResource>(context.getBackend(), size_, type)) { : gfx::OffscreenTexture(size_, std::make_unique<OffscreenTextureResource>(context.getBackend(), size_, type)) {
assert(!depth); assert(!depth);
assert(!stencil); assert(!stencil);
} }

View File

@@ -107,7 +107,7 @@ public:
wgpu::TextureView offscreenTextureView; wgpu::TextureView offscreenTextureView;
wgpu::Texture depthTexture; wgpu::Texture depthTexture;
wgpu::TextureView depthTextureView; wgpu::TextureView depthTextureView;
Size framebufferSize; Size offscreenTextureSize;
}; };
HeadlessBackend::HeadlessBackend(Size size_, SwapBehaviour swapBehaviour_, gfx::ContextMode mode) HeadlessBackend::HeadlessBackend(Size size_, SwapBehaviour swapBehaviour_, gfx::ContextMode mode)
@@ -116,8 +116,6 @@ HeadlessBackend::HeadlessBackend(Size size_, SwapBehaviour swapBehaviour_, gfx::
impl(std::make_unique<Impl>()) { impl(std::make_unique<Impl>()) {
static_cast<void>(swapBehaviour_); static_cast<void>(swapBehaviour_);
impl->framebufferSize = size_;
#if MLN_WEBGPU_IMPL_DAWN #if MLN_WEBGPU_IMPL_DAWN
// Initialize Dawn instance with TimedWaitAny feature enabled // Initialize Dawn instance with TimedWaitAny feature enabled
wgpu::InstanceFeatureName timedWaitAnyFeature = wgpu::InstanceFeatureName::TimedWaitAny; wgpu::InstanceFeatureName timedWaitAnyFeature = wgpu::InstanceFeatureName::TimedWaitAny;
@@ -221,6 +219,9 @@ HeadlessBackend::~HeadlessBackend() {
void HeadlessBackend::createOffscreenTextures() { void HeadlessBackend::createOffscreenTextures() {
if (!impl->device) return; if (!impl->device) return;
const auto renderableSize = getSize();
impl->offscreenTextureSize = renderableSize;
// Release old textures if they exist // Release old textures if they exist
impl->offscreenTextureView = nullptr; impl->offscreenTextureView = nullptr;
impl->offscreenTexture = nullptr; impl->offscreenTexture = nullptr;
@@ -236,8 +237,7 @@ void HeadlessBackend::createOffscreenTextures() {
#elif MLN_WEBGPU_IMPL_WGPU #elif MLN_WEBGPU_IMPL_WGPU
colorDesc.dimension = wgpu::TextureDimension::_2D; colorDesc.dimension = wgpu::TextureDimension::_2D;
#endif #endif
colorDesc.size = { colorDesc.size = {renderableSize.width, renderableSize.height, 1};
static_cast<uint32_t>(impl->framebufferSize.width), static_cast<uint32_t>(impl->framebufferSize.height), 1};
colorDesc.format = wgpu::TextureFormat::RGBA8Unorm; colorDesc.format = wgpu::TextureFormat::RGBA8Unorm;
colorDesc.mipLevelCount = 1; colorDesc.mipLevelCount = 1;
colorDesc.sampleCount = 1; colorDesc.sampleCount = 1;
@@ -282,8 +282,7 @@ void HeadlessBackend::createOffscreenTextures() {
#elif MLN_WEBGPU_IMPL_WGPU #elif MLN_WEBGPU_IMPL_WGPU
depthDesc.dimension = wgpu::TextureDimension::_2D; depthDesc.dimension = wgpu::TextureDimension::_2D;
#endif #endif
depthDesc.size = { depthDesc.size = {renderableSize.width, renderableSize.height, 1};
static_cast<uint32_t>(impl->framebufferSize.width), static_cast<uint32_t>(impl->framebufferSize.height), 1};
depthDesc.format = wgpu::TextureFormat::Depth24PlusStencil8; depthDesc.format = wgpu::TextureFormat::Depth24PlusStencil8;
depthDesc.mipLevelCount = 1; depthDesc.mipLevelCount = 1;
depthDesc.sampleCount = 1; depthDesc.sampleCount = 1;
@@ -323,8 +322,8 @@ void HeadlessBackend::createOffscreenTextures() {
gfx::Renderable& HeadlessBackend::getDefaultRenderable() { gfx::Renderable& HeadlessBackend::getDefaultRenderable() {
if (!hasResource()) { if (!hasResource()) {
setResource( setResource(std::make_unique<HeadlessRenderableResource>(
std::make_unique<HeadlessRenderableResource>(*this, static_cast<webgpu::Context&>(getContext()), size)); *this, static_cast<webgpu::Context&>(getContext()), getSize()));
} }
return *this; return *this;
} }
@@ -350,8 +349,7 @@ RendererBackend* HeadlessBackend::getRendererBackend() {
void HeadlessBackend::activate() { void HeadlessBackend::activate() {
active = true; active = true;
// Ensure textures are created if size changed // Ensure textures are created if size changed
if (impl && impl->framebufferSize != getSize()) { if (impl && impl->offscreenTextureSize != getSize()) {
impl->framebufferSize = getSize();
createOffscreenTextures(); createOffscreenTextures();
} }
} }
@@ -375,9 +373,6 @@ void* HeadlessBackend::getDepthStencilView() {
} }
mln::Size HeadlessBackend::getFramebufferSize() const { mln::Size HeadlessBackend::getFramebufferSize() const {
if (impl) {
return impl->framebufferSize;
}
return getSize(); return getSize();
} }

View File

@@ -354,7 +354,7 @@ OffscreenTexture::OffscreenTexture(
Context& context, const Size size_, const gfx::TextureChannelDataType type, bool depth, bool stencil) Context& context, const Size size_, const gfx::TextureChannelDataType type, bool depth, bool stencil)
: gfx::OffscreenTexture(size_, std::make_unique<OffscreenTextureResource>(context, size_, type, depth, stencil)) { : gfx::OffscreenTexture(size_, std::make_unique<OffscreenTextureResource>(context, size_, type, depth, stencil)) {
auto& offscreenResource = getResource<OffscreenTextureResource>(); auto& offscreenResource = getResource<OffscreenTextureResource>();
offscreenResource.setSizeListener([this](const Size& newSize) { size = newSize; }); offscreenResource.setSizeListener([this](const Size& newSize) { setRenderableSize(newSize); });
} }
bool OffscreenTexture::isRenderable() { bool OffscreenTexture::isRenderable() {

View File

@@ -21,6 +21,7 @@ add_library(
${PROJECT_SOURCE_DIR}/test/plugin/plugin.test.cpp ${PROJECT_SOURCE_DIR}/test/plugin/plugin.test.cpp
${PROJECT_SOURCE_DIR}/test/renderer/image_manager.test.cpp ${PROJECT_SOURCE_DIR}/test/renderer/image_manager.test.cpp
${PROJECT_SOURCE_DIR}/test/renderer/pattern_atlas.test.cpp ${PROJECT_SOURCE_DIR}/test/renderer/pattern_atlas.test.cpp
${PROJECT_SOURCE_DIR}/test/renderer/renderable.test.cpp
${PROJECT_SOURCE_DIR}/test/renderer/shader_registry.test.cpp ${PROJECT_SOURCE_DIR}/test/renderer/shader_registry.test.cpp
$<$<BOOL:${MLN_WITH_WEBGPU}>:${PROJECT_SOURCE_DIR}/test/renderer/wgsl_preprocessor.test.cpp> $<$<BOOL:${MLN_WITH_WEBGPU}>:${PROJECT_SOURCE_DIR}/test/renderer/wgsl_preprocessor.test.cpp>
${PROJECT_SOURCE_DIR}/test/sprite/sprite_loader.test.cpp ${PROJECT_SOURCE_DIR}/test/sprite/sprite_loader.test.cpp

View File

@@ -0,0 +1,23 @@
#include <mln/test/util.hpp>
#include <mln/gfx/renderable.hpp>
namespace {
class TestRenderable final : public mln::gfx::Renderable {
public:
explicit TestRenderable(mln::Size size_)
: Renderable(size_, nullptr) {}
void resize(const mln::Size& size_) { setRenderableSize(size_); }
};
} // namespace
TEST(Renderable, MaintainsCanonicalPhysicalSize) {
TestRenderable renderable({256, 128});
EXPECT_EQ(renderable.getSize(), (mln::Size{256, 128}));
renderable.resize({512, 384});
EXPECT_EQ(renderable.getSize(), (mln::Size{512, 384}));
}