Skip to content

Commit

Permalink
fixed window resize and depth clear
Browse files Browse the repository at this point in the history
  • Loading branch information
fabmax committed Feb 3, 2025
1 parent e1a8c48 commit 2f4963b
Show file tree
Hide file tree
Showing 11 changed files with 106 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ data class KoolConfigJvm(
val windowSize: Vec2i = Vec2i(1600, 900),
val isFullscreen: Boolean = false,
val showWindowOnStart: Boolean = true,
val updateOnWindowResize: Boolean = true,
val monitor: Int = -1,
val windowIcon: List<BufferedImage> = DEFAULT_ICON?.let { listOf(it) } ?: emptyList(),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.lwjgl.vulkan.VK10.*

class ClearHelper(val backend: RenderBackendVk) {
private val device: Device get() = backend.device
private val clearPipelines = mutableMapOf<RenderPassVk, ClearPipeline>()
private val clearPipelines = mutableMapOf<RenderPass, ClearPipeline>()

private val vertexModule: VkShaderModule = device.createShaderModule {
val spirv = checkNotNull(Shaderc.compileVertexShader(VERT_SRC, "clear-shader.vert", "main").spirvData)
Expand All @@ -25,13 +25,13 @@ class ClearHelper(val backend: RenderBackendVk) {
}

fun clear(passEncoderState: PassEncoderState) {
val clearPipeline = clearPipelines.getOrPut(passEncoderState.gpuRenderPass) {
ClearPipeline(passEncoderState.gpuRenderPass)
val clearPipeline = clearPipelines.getOrPut(passEncoderState.renderPass) {
ClearPipeline(passEncoderState.renderPass, passEncoderState.gpuRenderPass)
}
clearPipeline.clear(passEncoderState)
}

private inner class ClearPipeline(val gpuRenderPass: RenderPassVk) : BaseReleasable() {
private inner class ClearPipeline(val renderPass: RenderPass, val gpuRenderPass: RenderPassVk) : BaseReleasable() {
val descriptorSetLayout: VkDescriptorSetLayout
val pipelineLayout: VkPipelineLayout
val bindGroupData: BindGroupDataVk
Expand All @@ -40,9 +40,8 @@ class ClearHelper(val backend: RenderBackendVk) {
var prevColor: Color? = null
var prevDepth = 0f

val clearColorOnly: VkGraphicsPipeline by lazy { makeClearPipeline(true, false) }
val clearDepthOnly: VkGraphicsPipeline by lazy { makeClearPipeline(false, true) }
val clearColorAndDepth: VkGraphicsPipeline by lazy { makeClearPipeline(true, true) }
val clearColorOnly: VkGraphicsPipeline by lazy { makeClearPipeline(false) }
val clearColorAndDepth: VkGraphicsPipeline by lazy { makeClearPipeline(true) }

init {
val bindGrpLayout = BindGroupLayout.Builder(0, BindGroupScope.VIEW).apply {
Expand Down Expand Up @@ -71,33 +70,51 @@ class ClearHelper(val backend: RenderBackendVk) {
bindGroupData = BindGroupDataVk(bindGrpData, descriptorSetLayout, backend)
}

releaseWith(gpuRenderPass)
releaseWith(renderPass)
}

override fun release() {
super.release()
cancelReleaseWith(gpuRenderPass)
cancelReleaseWith(renderPass)
bindGroupData.release()
device.destroyPipelineLayout(pipelineLayout)
device.destroyDescriptorSetLayout(descriptorSetLayout)
}

fun clear(passEncoderState: PassEncoderState) {
val rp = passEncoderState.renderPass
val clearColor = (rp.clearColors[0] as? ClearColorFill)?.clearColor
val clearColor = (rp.clearColors[0] as? ClearColorFill)?.clearColor ?: CLEAR_COLOR_EMPTY
val clearDepth = if (rp.isReverseDepth) 0f else 1f

with(passEncoderState.stack) {
val view = rp.views.first()
val viewport = callocVkViewportN(1) {
x(view.viewport.x.toFloat())
y(view.viewport.y.toFloat())
width(view.viewport.width.toFloat())
height(view.viewport.height.toFloat())
minDepth(0f)
maxDepth(1f)
}
vkCmdSetViewport(passEncoderState.commandBuffer, 0, viewport)

val scissor = callocVkRect2DN(1) {
offset { it.set(view.viewport.x, view.viewport.y) }
extent { it.set(view.viewport.width, view.viewport.height) }
}
vkCmdSetScissor(passEncoderState.commandBuffer, 0, scissor)
}

if (clearColor != prevColor || clearDepth != prevDepth) {
prevColor = clearColor
prevDepth = clearDepth
clearValues.buffer.clear()
clearColor?.putTo(clearValues.buffer)
clearColor.putTo(clearValues.buffer)
clearValues.buffer.putFloat32(clearDepth)
clearValues.markDirty()
}

val clearPipeline = when {
clearColor == null -> clearDepthOnly
rp.clearDepth != ClearDepthFill -> clearColorOnly
else -> clearColorAndDepth
}
Expand All @@ -108,7 +125,7 @@ class ClearHelper(val backend: RenderBackendVk) {
vkCmdDraw(passEncoderState.commandBuffer, 4, 1, 0, 0)
}

private fun makeClearPipeline(isClearColor: Boolean, isClearDepth: Boolean): VkGraphicsPipeline = memStack {
private fun makeClearPipeline(isClearDepth: Boolean): VkGraphicsPipeline = memStack {
val shaderStageInfos = callocVkPipelineShaderStageCreateInfoN(2) {
this[0]
.stage(VK_SHADER_STAGE_VERTEX_BIT)
Expand All @@ -130,6 +147,7 @@ class ClearHelper(val backend: RenderBackendVk) {
cullMode(CullMethod.NO_CULLING.vk)
}
val depthStencil = callocVkPipelineDepthStencilStateCreateInfo {
depthTestEnable(true)
depthWriteEnable(isClearDepth)
depthCompareOp(DepthCompareOp.ALWAYS.vk)
}
Expand All @@ -145,10 +163,14 @@ class ClearHelper(val backend: RenderBackendVk) {
pDynamicStates(ints(VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR))
}
val blendAttachments = callocVkPipelineColorBlendAttachmentStateN(1) {
if (isClearColor) {
colorWriteMask(VK_COLOR_COMPONENT_R_BIT or VK_COLOR_COMPONENT_G_BIT or VK_COLOR_COMPONENT_B_BIT or VK_COLOR_COMPONENT_A_BIT)
}
blendEnable(false)
colorWriteMask(VK_COLOR_COMPONENT_R_BIT or VK_COLOR_COMPONENT_G_BIT or VK_COLOR_COMPONENT_B_BIT or VK_COLOR_COMPONENT_A_BIT)
blendEnable(true)
srcColorBlendFactor(VK_BLEND_FACTOR_ZERO)
dstColorBlendFactor(VK_BLEND_FACTOR_ONE)
colorBlendOp(VK_BLEND_OP_ADD)
srcAlphaBlendFactor(VK_BLEND_FACTOR_ZERO)
dstAlphaBlendFactor(VK_BLEND_FACTOR_ONE)
alphaBlendOp(VK_BLEND_OP_ADD)
}
val blendInfo = callocVkPipelineColorBlendStateCreateInfo {
pAttachments(blendAttachments)
Expand All @@ -161,7 +183,9 @@ class ClearHelper(val backend: RenderBackendVk) {
colorFormats.put(i, gpuRenderPass.colorTargetFormats[i])
}
pColorAttachmentFormats(colorFormats)
depthAttachmentFormat(backend.physicalDevice.depthFormat)
if (gpuRenderPass.hasDepth) {
depthAttachmentFormat(backend.physicalDevice.depthFormat)
}
}

device.createGraphicsPipeline {
Expand All @@ -185,6 +209,7 @@ class ClearHelper(val backend: RenderBackendVk) {
}

companion object {
private val CLEAR_COLOR_EMPTY = Color.BLACK.withAlpha(0f)
private val VERT_SRC = """
#version 450
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package de.fabmax.kool.pipeline.backend.vk

import de.fabmax.kool.KoolSystem
import de.fabmax.kool.configJvm
import de.fabmax.kool.math.Vec2i
import de.fabmax.kool.math.clamp
import de.fabmax.kool.util.*
import org.lwjgl.system.MemoryStack
Expand All @@ -17,7 +18,6 @@ class PhysicalDevice(val backend: RenderBackendVk) : BaseReleasable() {

val vkPhysicalDevice: VkPhysicalDevice
val queueFamiliyIndices: QueueFamilyIndices
val swapChainSupport: SwapChainSupportDetails
val deviceProperties = VkPhysicalDeviceProperties.calloc()
val vkDeviceFeatures2 = VkPhysicalDeviceFeatures2.calloc().apply { `sType$Default`() }
val deviceFeatures: VkPhysicalDeviceFeatures get() = vkDeviceFeatures2.features()
Expand All @@ -38,18 +38,14 @@ class PhysicalDevice(val backend: RenderBackendVk) : BaseReleasable() {

val depthFormat: Int

private val selectedDevice: PhysicalDeviceWrapper
private val imgFormatTilingFeatures = mutableMapOf<Int, Int>()

init {
memStack {
val devPtrs = enumeratePointers { cnt, ptrs -> vkEnumeratePhysicalDevices(backend.instance.vkInstance, cnt, ptrs) }
val devices = (0 until devPtrs.capacity()).map { PhysicalDeviceWrapper(devPtrs[it], this) }
val selectedDevice = selectPhysicalDevice(devices)

val stackSwapChain = selectedDevice.querySwapChainSupport(this)
swapChainSupport = selectedDevice.querySwapChainSupport(this,
VkSurfaceCapabilitiesKHR.malloc(), VkSurfaceFormatKHR.malloc(stackSwapChain.formats.size)
)
selectedDevice = selectPhysicalDevice(devices)

availableDeviceExtensions = selectedDevice.availableExtensions
isPortabilityDevice = "VK_KHR_portability_subset" in availableDeviceExtensions
Expand Down Expand Up @@ -111,6 +107,17 @@ class PhysicalDevice(val backend: RenderBackendVk) : BaseReleasable() {
releaseWith(backend.instance)
}

fun querySurfaceFormat(): VkSurfaceFormatKHR {
return memStack {
val swapChainInfo = selectedDevice.querySwapChainSupport(this)
swapChainInfo.chooseSurfaceFormat()
}
}

fun querySwapchainSupport(stack: MemoryStack): SwapChainSupportDetails {
return selectedDevice.querySwapChainSupport(stack)
}

private fun MemoryStack.selectPhysicalDevice(devices: List<PhysicalDeviceWrapper>): PhysicalDeviceWrapper {
val suitableDevices = devices.filter {
it.queueFamiliyIndices.isComplete &&
Expand Down Expand Up @@ -319,7 +326,7 @@ class PhysicalDevice(val backend: RenderBackendVk) : BaseReleasable() {
it.format() == VK_FORMAT_B8G8R8A8_UNORM && it.colorSpace() == preferredColorSpace
}?.let {
selectedSurfaceFmt = it
logI("PhysicalDevice") { "Selected surface format with preferred color space ${backend.setup.preferredColorSpace}" }
logT("PhysicalDevice") { "Selected surface format with preferred color space ${backend.setup.preferredColorSpace}" }
return it
}

Expand All @@ -345,15 +352,15 @@ class PhysicalDevice(val backend: RenderBackendVk) : BaseReleasable() {
}
}

fun chooseSwapExtent(window: GlfwVkWindow, stack: MemoryStack): VkExtent2D {
fun chooseSwapExtent(window: GlfwVkWindow): Vec2i {
val minWidth = capabilities.minImageExtent().width()
val maxWidth = capabilities.maxImageExtent().width()
val minHeight = capabilities.minImageExtent().height()
val maxHeight = capabilities.maxImageExtent().height()

return VkExtent2D.malloc(stack)
.width(window.framebufferWidth.clamp(minWidth, maxWidth))
.height(window.framebufferHeight.clamp(minHeight, maxHeight))
return Vec2i(
window.framebufferWidth.clamp(minWidth, maxWidth),
window.framebufferHeight.clamp(minHeight, maxHeight)
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class RenderBackendVk(val ctx: Lwjgl3Context) : RenderBackendJvm {
ReleaseQueue.processQueue()

passEncoderState.beginFrame(this)
timestampQueryPool.onBeginFrame()
frameTimer.begin(passEncoderState.commandBuffer)

ctx.preparePipelines(passEncoderState)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,14 @@ abstract class RenderPassVk(
}
}

val isCmdValid = cmd.isActive && cmd.geometry.numIndices > 0
val bindSuccessful = backend.pipelineManager.bindDrawPipeline(cmd, passEncoderState)
if (isCmdValid && bindSuccessful) {
val insts = cmd.instances
val insts = cmd.instances
val isCmdValid = cmd.isActive && cmd.geometry.numIndices > 0 && (insts == null || insts.numInstances > 0)
val bindSuccessful = isCmdValid && backend.pipelineManager.bindDrawPipeline(cmd, passEncoderState)
if (bindSuccessful) {
if (insts == null) {
BackendStats.addDrawCommands(1, cmd.geometry.numPrimitives)
vkCmdDrawIndexed(passEncoderState.commandBuffer, cmd.geometry.numIndices, 1, 0, 0, 0)
} else if (insts.numInstances > 0) {
} else {
BackendStats.addDrawCommands(1, cmd.geometry.numPrimitives * insts.numInstances)
vkCmdDrawIndexed(passEncoderState.commandBuffer, cmd.geometry.numIndices, insts.numInstances, 0, 0, 0)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import org.lwjgl.vulkan.VkCommandBuffer
class ScreenPassVk(backend: RenderBackendVk) :
RenderPassVk(true, backend.swapchain.numSamples, backend)
{
override val colorTargetFormats: List<Int> = listOf(backend.physicalDevice.swapChainSupport.chooseSurfaceFormat().format())
private val _colorTargetFormats = mutableListOf(backend.physicalDevice.querySurfaceFormat().format())
override val colorTargetFormats: List<Int> get() = _colorTargetFormats
private var isStore = false

private lateinit var colorImage: ImageVk
Expand All @@ -37,6 +38,8 @@ class ScreenPassVk(backend: RenderBackendVk) :
}

fun onSwapchainRecreated() {
// todo: if this would really change, draw pipelines would also need to be recreated...
_colorTargetFormats[0] = backend.physicalDevice.querySurfaceFormat().format()
backend.commandPool.singleShotCommands { commandBuffer ->
val (cImage, cImageView) = createColorResources(numSamples, commandBuffer)
colorImage = cImage.also { it.releaseWith(backend.swapchain) }
Expand Down Expand Up @@ -205,7 +208,7 @@ class ScreenPassVk(backend: RenderBackendVk) :

val imgInfo = ImageInfo(
imageType = VK_IMAGE_TYPE_2D,
format = backend.physicalDevice.swapChainSupport.chooseSurfaceFormat().format(),
format = backend.physicalDevice.querySurfaceFormat().format(),
width = colorImage.width,
height = colorImage.height,
depth = 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package de.fabmax.kool.pipeline.backend.vk

import de.fabmax.kool.KoolSystem
import de.fabmax.kool.configJvm
import de.fabmax.kool.math.Vec2i
import de.fabmax.kool.util.*
import org.lwjgl.BufferUtils
import org.lwjgl.system.MemoryStack
import org.lwjgl.vulkan.KHRSurface.VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
import org.lwjgl.vulkan.KHRSwapchain.*
import org.lwjgl.vulkan.VK10.*
import org.lwjgl.vulkan.VkExtent2D

class Swapchain(val backend: RenderBackendVk) : BaseReleasable() {

Expand All @@ -22,13 +22,13 @@ class Swapchain(val backend: RenderBackendVk) : BaseReleasable() {

val vkSwapchain: VkSwapchain
val imageFormat: Int
val extent: VkExtent2D = VkExtent2D.malloc()
val images: List<VkImage>
val imageViews: List<VkImageView>
val numSamples = backend.physicalDevice.maxSamples.coerceAtMost(KoolSystem.configJvm.numSamples)

val width: Int get() = extent.width()
val height: Int get() = extent.height()
val extent: Vec2i
val width: Int get() = extent.x
val height: Int get() = extent.y

private val imageAvailableSemas: List<VkSemaphore>
private val renderFinishedSemas: List<VkSemaphore>
Expand All @@ -40,10 +40,10 @@ class Swapchain(val backend: RenderBackendVk) : BaseReleasable() {

init {
memStack {
val swapChainSupport = physicalDevice.swapChainSupport
val swapChainSupport = physicalDevice.querySwapchainSupport(this)
val surfaceFormat = swapChainSupport.chooseSurfaceFormat()
val presentMode = swapChainSupport.choosePresentationMode()
val extent = swapChainSupport.chooseSwapExtent(backend.glfwWindow, this)
extent = swapChainSupport.chooseSwapExtent(backend.glfwWindow)
var imageCount = swapChainSupport.capabilities.minImageCount() + 1
if (swapChainSupport.capabilities.maxImageCount() > 0) {
imageCount = imageCount.coerceAtMost(swapChainSupport.capabilities.maxImageCount())
Expand All @@ -54,7 +54,7 @@ class Swapchain(val backend: RenderBackendVk) : BaseReleasable() {
minImageCount(imageCount)
imageFormat(surfaceFormat.format())
imageColorSpace(surfaceFormat.colorSpace())
imageExtent(extent)
imageExtent { it.set(extent.x, extent.y) }
imageArrayLayers(1)
imageUsage(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT or VK_IMAGE_USAGE_TRANSFER_DST_BIT)
preTransform(swapChainSupport.capabilities.currentTransform())
Expand All @@ -72,7 +72,6 @@ class Swapchain(val backend: RenderBackendVk) : BaseReleasable() {
}

imageFormat = surfaceFormat.format()
this@Swapchain.extent.set(extent)

val imgs = enumerateLongs { cnt, imgs ->
vkGetSwapchainImagesKHR(device.vkDevice, vkSwapchain.handle, cnt, imgs)
Expand Down Expand Up @@ -143,7 +142,6 @@ class Swapchain(val backend: RenderBackendVk) : BaseReleasable() {
super.release()
cancelReleaseWith(backend.device)
device.destroySwapchain(vkSwapchain)
extent.free()

imageViews.forEach { device.destroyImageView(it) }

Expand Down
Loading

0 comments on commit 2f4963b

Please sign in to comment.