diff --git a/include/mbgl/vulkan/renderable_resource.hpp b/include/mbgl/vulkan/renderable_resource.hpp index 5657246cd..ca675627f 100644 --- a/include/mbgl/vulkan/renderable_resource.hpp +++ b/include/mbgl/vulkan/renderable_resource.hpp @@ -43,6 +43,9 @@ protected: void initSwapchain(uint32_t w, uint32_t h); void initDepthStencil(); + void initRenderPass(); + void setColorFormat(vk::Format format); + void setDepthFormat(vk::Format format); void swap() override; diff --git a/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.cpp b/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.cpp index 17ce25b0c..949cc862d 100644 --- a/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.cpp +++ b/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.cpp @@ -77,8 +77,7 @@ std::vector AndroidVulkanRendererBackend::getInstanceExtensions() { return extensions; } -void AndroidVulkanRendererBackend::resizeFramebuffer(int width, int height) { - size = {static_cast(width), static_cast(height)}; +void AndroidVulkanRendererBackend::resizeFramebuffer(int, int) { if (context) { static_cast(*context).requestSurfaceUpdate(); } diff --git a/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/surfaceview/SurfaceViewResizeActivity.kt b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/surfaceview/SurfaceViewResizeActivity.kt index 7a04b3102..4c5cf68f6 100644 --- a/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/surfaceview/SurfaceViewResizeActivity.kt +++ b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/surfaceview/SurfaceViewResizeActivity.kt @@ -1,9 +1,9 @@ package org.maplibre.android.testapp.activity.surfaceview +import android.animation.ValueAnimator import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity -import androidx.coordinatorlayout.widget.CoordinatorLayout import com.google.android.material.floatingactionbutton.FloatingActionButton import org.maplibre.android.maps.* import org.maplibre.android.testapp.R @@ -14,6 +14,8 @@ import org.maplibre.android.testapp.styles.TestStyles */ class SurfaceViewResizeActivity : AppCompatActivity() { private lateinit var mapView: MapView + private var animatedValue: ValueAnimator? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_surfaceview_resize) @@ -37,18 +39,61 @@ class SurfaceViewResizeActivity : AppCompatActivity() { } private fun setupMap(maplibreMap: MapLibreMap) { - maplibreMap.setStyle(TestStyles.AWS_OPEN_DATA_STANDARD_LIGHT) + maplibreMap.setStyle(TestStyles.getPredefinedStyleWithFallback("Streets")) } private fun setupFab() { val fabDebug = findViewById(R.id.fabResize) fabDebug.setOnClickListener { view: View? -> + val parent = findViewById(R.id.coordinator_layout) + val params = mapView.layoutParams + + if (animatedValue != null) { + animatedValue?.cancel() + animatedValue = null + + params.width = parent.width + params.height = parent.height + } + if (this::mapView.isInitialized) { - val parent = findViewById(R.id.coordinator_layout) - val width = if (parent.width == mapView.width) parent.width / 2 else parent.width - val height = - if (parent.height == mapView.height) parent.height / 2 else parent.height - mapView.layoutParams = CoordinatorLayout.LayoutParams(width, height) + params.width = if (parent.width == params.width) parent.width / 2 else parent.width + params.height = + if (parent.height == params.height) parent.height / 2 else parent.height + } + + mapView.requestLayout() + } + + val fabAnimatedDebug = findViewById(R.id.fabAnimatedResize) + fabAnimatedDebug.setOnClickListener { view: View? -> + val parent = findViewById(R.id.coordinator_layout) + + if (animatedValue != null) { + animatedValue?.cancel() + animatedValue = null + + mapView.layoutParams.width = parent.width + mapView.layoutParams.height = parent.height + mapView.requestLayout() + } else if (this::mapView.isInitialized) { + val minValue = parent.height / 2 + val maxValue = parent.height + + animatedValue = ValueAnimator.ofInt(maxValue, minValue) + animatedValue?.apply { + duration = 1000 + repeatCount = ValueAnimator.INFINITE + repeatMode = ValueAnimator.REVERSE + + addUpdateListener { animator -> + val height = animator.animatedValue as Int + mapView.layoutParams.height = height + mapView.requestLayout() + } + + start() + } } } } diff --git a/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/textureview/TextureViewResizeActivity.kt b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/textureview/TextureViewResizeActivity.kt index a9aa9917e..14afd0018 100644 --- a/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/textureview/TextureViewResizeActivity.kt +++ b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/textureview/TextureViewResizeActivity.kt @@ -1,5 +1,6 @@ package org.maplibre.android.testapp.activity.textureview +import android.animation.ValueAnimator import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity @@ -14,6 +15,8 @@ import org.maplibre.android.testapp.styles.TestStyles */ class TextureViewResizeActivity : AppCompatActivity() { private lateinit var mapView: MapView + private var animatedValue: ValueAnimator? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_textureview_resize) @@ -43,12 +46,55 @@ class TextureViewResizeActivity : AppCompatActivity() { private fun setupFab() { val fabDebug = findViewById(R.id.fabResize) fabDebug.setOnClickListener { view: View? -> + val parent = findViewById(R.id.coordinator_layout) + val params = mapView.layoutParams + + if (animatedValue != null) { + animatedValue?.cancel() + animatedValue = null + + params.width = parent.width + params.height = parent.height + } + if (this::mapView.isInitialized) { - val parent = findViewById(R.id.coordinator_layout) - val width = if (parent.width == mapView.width) parent.width / 2 else parent.width - val height = - if (parent.height == mapView.height) parent.height / 2 else parent.height - mapView.layoutParams = CoordinatorLayout.LayoutParams(width, height) + params.width = if (parent.width == params.width) parent.width / 2 else parent.width + params.height = + if (parent.height == params.height) parent.height / 2 else parent.height + } + + mapView.requestLayout() + } + + val fabAnimatedDebug = findViewById(R.id.fabAnimatedResize) + fabAnimatedDebug.setOnClickListener { view: View? -> + val parent = findViewById(R.id.coordinator_layout) + + if (animatedValue != null) { + animatedValue?.cancel() + animatedValue = null + + mapView.layoutParams.width = parent.width + mapView.layoutParams.height = parent.height + mapView.requestLayout() + } else if (this::mapView.isInitialized) { + val minValue = parent.height / 2 + val maxValue = parent.height + + animatedValue = ValueAnimator.ofInt(maxValue, minValue) + animatedValue?.apply { + duration = 1000 + repeatCount = ValueAnimator.INFINITE + repeatMode = ValueAnimator.REVERSE + + addUpdateListener { animator -> + val height = animator.animatedValue as Int + mapView.layoutParams.height = height + mapView.requestLayout() + } + + start() + } } } } diff --git a/platform/android/MapLibreAndroidTestApp/src/main/res/layout/activity_surfaceview_resize.xml b/platform/android/MapLibreAndroidTestApp/src/main/res/layout/activity_surfaceview_resize.xml index 9731cbf45..7adc3230c 100644 --- a/platform/android/MapLibreAndroidTestApp/src/main/res/layout/activity_surfaceview_resize.xml +++ b/platform/android/MapLibreAndroidTestApp/src/main/res/layout/activity_surfaceview_resize.xml @@ -14,6 +14,17 @@ android:layout_height="match_parent" app:maplibre_renderTextureMode="false"/> + + + + createSwapchainKHRUnique(swapchainCreateInfo, nullptr, dispatcher); swapchainImages = device->getSwapchainImagesKHR(swapchain.get(), dispatcher); - colorFormat = swapchainCreateInfo.imageFormat; + setColorFormat(swapchainCreateInfo.imageFormat); extent = swapchainCreateInfo.imageExtent; acquireSemaphores.reserve(swapchainImages.size()); @@ -193,7 +193,7 @@ void SurfaceRenderableResource::initDepthStencil() { return; } - depthFormat = *formatIt; + setDepthFormat(*formatIt); const auto imageUsage = vk::ImageUsageFlags() | vk::ImageUsageFlagBits::eDepthStencilAttachment | vk::ImageUsageFlagBits::eTransientAttachment; @@ -242,6 +242,94 @@ void SurfaceRenderableResource::initDepthStencil() { backend.setDebugName(depthAllocation->imageView.get(), "SwapchainDepthImageView"); } +void SurfaceRenderableResource::initRenderPass() { + // The current render pass should be invalidated if: + // - color/depth format changes (use setColorFormat/setDepthFormat) + // - renderable resource type changes + // - this is done currently only by inheritance and it can't change during runtime + + if (renderPass) { + return; + } + + const auto& device = backend.getDevice(); + const auto& dispatcher = backend.getDispatcher(); + const auto colorLayout = surface ? vk::ImageLayout::ePresentSrcKHR : vk::ImageLayout::eTransferSrcOptimal; + + const std::array attachments = { + vk::AttachmentDescription() + .setFormat(colorFormat) + .setSamples(vk::SampleCountFlagBits::e1) + .setLoadOp(vk::AttachmentLoadOp::eClear) + .setStoreOp(vk::AttachmentStoreOp::eStore) + .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare) + .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare) + .setInitialLayout(vk::ImageLayout::eUndefined) + .setFinalLayout(colorLayout), + + vk::AttachmentDescription() + .setFormat(depthFormat) + .setSamples(vk::SampleCountFlagBits::e1) + .setLoadOp(vk::AttachmentLoadOp::eClear) + .setStoreOp(vk::AttachmentStoreOp::eDontCare) + .setStencilLoadOp(vk::AttachmentLoadOp::eClear) + .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare) + .setInitialLayout(vk::ImageLayout::eUndefined) + .setFinalLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal)}; + + const vk::AttachmentReference colorAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal); + const vk::AttachmentReference depthAttachmentRef(1, vk::ImageLayout::eDepthStencilAttachmentOptimal); + + const auto subpass = vk::SubpassDescription() + .setPipelineBindPoint(vk::PipelineBindPoint::eGraphics) + .setColorAttachmentCount(1) + .setColorAttachments(colorAttachmentRef) + .setPDepthStencilAttachment(&depthAttachmentRef); + + const std::array dependencies = { + vk::SubpassDependency() + .setSrcSubpass(VK_SUBPASS_EXTERNAL) + .setDstSubpass(0) + .setSrcStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput) + .setDstStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput) + .setSrcAccessMask({}) + .setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite), + + vk::SubpassDependency() + .setSrcSubpass(VK_SUBPASS_EXTERNAL) + .setDstSubpass(0) + .setSrcStageMask(vk::PipelineStageFlagBits::eEarlyFragmentTests | + vk::PipelineStageFlagBits::eLateFragmentTests) + .setDstStageMask(vk::PipelineStageFlagBits::eEarlyFragmentTests | + vk::PipelineStageFlagBits::eLateFragmentTests) + .setSrcAccessMask({}) + .setDstAccessMask(vk::AccessFlagBits::eDepthStencilAttachmentWrite), + }; + + const auto renderPassCreateInfo = + vk::RenderPassCreateInfo().setAttachments(attachments).setSubpasses(subpass).setDependencies(dependencies); + + renderPass = device->createRenderPassUnique(renderPassCreateInfo, nullptr, dispatcher); +} + +void SurfaceRenderableResource::setColorFormat(vk::Format format) { + if (colorFormat == format) { + return; + } + + renderPass.reset(); + colorFormat = format; +} + +void SurfaceRenderableResource::setDepthFormat(vk::Format format) { + if (depthFormat == format) { + return; + } + + renderPass.reset(); + depthFormat = format; +} + void SurfaceRenderableResource::swap() { auto& context = static_cast(backend.getContext()); context.submitFrame(); @@ -327,62 +415,7 @@ void SurfaceRenderableResource::init(uint32_t w, uint32_t h) { initDepthStencil(); // create render pass - const auto colorLayout = surface ? vk::ImageLayout::ePresentSrcKHR : vk::ImageLayout::eTransferSrcOptimal; - - const std::array attachments = { - vk::AttachmentDescription() - .setFormat(colorFormat) - .setSamples(vk::SampleCountFlagBits::e1) - .setLoadOp(vk::AttachmentLoadOp::eClear) - .setStoreOp(vk::AttachmentStoreOp::eStore) - .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare) - .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare) - .setInitialLayout(vk::ImageLayout::eUndefined) - .setFinalLayout(colorLayout), - - vk::AttachmentDescription() - .setFormat(depthFormat) - .setSamples(vk::SampleCountFlagBits::e1) - .setLoadOp(vk::AttachmentLoadOp::eClear) - .setStoreOp(vk::AttachmentStoreOp::eDontCare) - .setStencilLoadOp(vk::AttachmentLoadOp::eClear) - .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare) - .setInitialLayout(vk::ImageLayout::eUndefined) - .setFinalLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal)}; - - const vk::AttachmentReference colorAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal); - const vk::AttachmentReference depthAttachmentRef(1, vk::ImageLayout::eDepthStencilAttachmentOptimal); - - const auto subpass = vk::SubpassDescription() - .setPipelineBindPoint(vk::PipelineBindPoint::eGraphics) - .setColorAttachmentCount(1) - .setColorAttachments(colorAttachmentRef) - .setPDepthStencilAttachment(&depthAttachmentRef); - - const std::array dependencies = { - vk::SubpassDependency() - .setSrcSubpass(VK_SUBPASS_EXTERNAL) - .setDstSubpass(0) - .setSrcStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput) - .setDstStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput) - .setSrcAccessMask({}) - .setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite), - - vk::SubpassDependency() - .setSrcSubpass(VK_SUBPASS_EXTERNAL) - .setDstSubpass(0) - .setSrcStageMask(vk::PipelineStageFlagBits::eEarlyFragmentTests | - vk::PipelineStageFlagBits::eLateFragmentTests) - .setDstStageMask(vk::PipelineStageFlagBits::eEarlyFragmentTests | - vk::PipelineStageFlagBits::eLateFragmentTests) - .setSrcAccessMask({}) - .setDstAccessMask(vk::AccessFlagBits::eDepthStencilAttachmentWrite), - }; - - const auto renderPassCreateInfo = - vk::RenderPassCreateInfo().setAttachments(attachments).setSubpasses(subpass).setDependencies(dependencies); - - renderPass = device->createRenderPassUnique(renderPassCreateInfo, nullptr, dispatcher); + initRenderPass(); // create swapchain framebuffers swapchainFramebuffers.reserve(swapchainImageViews.size()); @@ -408,7 +441,6 @@ void SurfaceRenderableResource::recreateSwapchain() { backend.getDevice()->waitIdle(backend.getDispatcher()); swapchainFramebuffers.clear(); - renderPass.reset(); swapchainImageViews.clear(); swapchainImages.clear(); acquireSemaphores.clear();