Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Per-node UBO population and per-frame cam state access #676

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions src/main/kotlin/graphics/scenery/Camera.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

import graphics.scenery.primitives.TextBoard
import graphics.scenery.attribute.material.HasMaterial
import graphics.scenery.attribute.populatesubo.DefaultPopulatesUBO
import graphics.scenery.attribute.populatesubo.HasCustomPopulatesUBO
import graphics.scenery.attribute.populatesubo.HasPopulatesUBO
import graphics.scenery.attribute.populatesubo.PopulatesUBO
import graphics.scenery.attribute.renderable.HasRenderable
import graphics.scenery.attribute.spatial.DefaultSpatial
import graphics.scenery.attribute.spatial.HasCustomSpatial
import graphics.scenery.backends.UBO
import graphics.scenery.net.Networkable
import graphics.scenery.utils.extensions.minus
import graphics.scenery.utils.extensions.plus
import graphics.scenery.utils.extensions.times
import graphics.scenery.utils.extensions.xyz
import graphics.scenery.utils.extensions.*
import org.joml.*
import java.lang.Math
import java.util.concurrent.atomic.AtomicInteger
Expand All @@ -26,7 +28,11 @@
* @constructor Creates a new camera with default position and right-handed
* coordinate system.
*/
open class Camera : DefaultNode("Camera"), HasRenderable, HasMaterial, HasCustomSpatial<Camera.CameraSpatial> {
open class Camera : DefaultNode("Camera"),

Check warning on line 31 in src/main/kotlin/graphics/scenery/Camera.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/Camera.kt#L31

Class 'Camera' with '19' functions detected. Defined threshold inside classes is set to '11'
HasRenderable,
HasMaterial,
HasCustomSpatial<Camera.CameraSpatial>,
HasCustomPopulatesUBO<Camera.CameraUBOPopulator> {

/** Enum class for camera projection types */
enum class ProjectionType {
Expand Down Expand Up @@ -88,22 +94,30 @@
/** Disables culling for this camera. */
var disableCulling: Boolean = false

/** The eye count (aka number of generated viewports) of the current camera. */
var eyeCount = 1
protected set

var wantsSync = true
override fun wantsSync(): Boolean = wantsSync

init {
this.nodeType = "Camera"
this.viewSpaceTripod = cameraTripod()
this.name = "Camera-${counter.incrementAndGet()}"
addSpatial()
addRenderable()
addMaterial()
addPopulatesUBO()
}

override fun createSpatial(): CameraSpatial {
return CameraSpatial(this)
}

override fun createPopulatesUBO(): PopulatesUBO {
return CameraUBOPopulator(this)
}

override fun update(fresh: Networkable, getNetworkable: (Int) -> Networkable, additionalData: Any?) {
if (fresh !is Camera) throw IllegalArgumentException("Update called with object of foreign class")
super.update(fresh, getNetworkable, additionalData)
Expand Down Expand Up @@ -367,6 +381,19 @@
protected val counter = AtomicInteger(0)
}

open class CameraUBOPopulator(open val cam: Camera): DefaultPopulatesUBO() {

Check warning on line 384 in src/main/kotlin/graphics/scenery/Camera.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/Camera.kt#L384

CameraUBOPopulator is missing required documentation.
override fun populate(ubo: UBO) {
val camSpatial = cam.spatial()

ubo.add("projection0", { camSpatial.projection.applyVulkanCoordinateSystem() })
ubo.add("projection1", { camSpatial.projection.applyVulkanCoordinateSystem() })
ubo.add("inverseProjection0", { camSpatial.projection.applyVulkanCoordinateSystem().invert() })
ubo.add("inverseProjection1", { camSpatial.projection.applyVulkanCoordinateSystem().invert() })
ubo.add("headShift", { Matrix4f().identity() })
ubo.add("IPD", { 0.05f })
ubo.add("stereoEnabled", { 0 })
}
}

open class CameraSpatial(val camera: Camera): DefaultSpatial(camera) {

Expand Down
1 change: 0 additions & 1 deletion src/main/kotlin/graphics/scenery/DefaultNode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ open class DefaultNode(name: String = "Node") : Node, Networkable {
return true
}

override var nodeType = "Node"
Copy link
Contributor

@smlpt smlpt Feb 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this cause any trouble in other parts of scenery that rely on this node type?
Or rather: will removing the property nodeType altogether result in any unexpected behavior in other parts of scenery?

override var boundingBox: OrientedBoundingBox? = null
override val logger by lazyLogger()

Expand Down
40 changes: 36 additions & 4 deletions src/main/kotlin/graphics/scenery/DetachedHeadCamera.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package graphics.scenery

import graphics.scenery.attribute.populatesubo.HasCustomPopulatesUBO
import graphics.scenery.attribute.populatesubo.PopulatesUBO
import graphics.scenery.backends.Display
import graphics.scenery.backends.UBO
import graphics.scenery.controls.TrackerInput
import graphics.scenery.utils.extensions.applyVulkanCoordinateSystem
import graphics.scenery.utils.extensions.plus
import graphics.scenery.utils.extensions.times
import org.joml.Matrix4f
Expand All @@ -20,7 +24,11 @@
* @author Ulrik Günther <[email protected]>
*/

class DetachedHeadCamera(@Transient var tracker: TrackerInput? = null) : Camera() {
open class DetachedHeadCamera(@Transient var tracker: TrackerInput? = null) : Camera(), HasCustomPopulatesUBO<Camera.CameraUBOPopulator> {

init {
eyeCount = 2
}

override var width: Int = 0
get() = if (tracker != null && tracker is Display && tracker?.initializedAndWorking() == true) {
Expand Down Expand Up @@ -104,12 +112,36 @@
val headOrientation: Quaternionf by HeadOrientationDelegate()

init {
this.nodeType = "Camera"
this.name = "DetachedHeadCamera-${tracker ?: "${counter.getAndIncrement()}"}"
name = "DetachedHeadCamera-${tracker ?: "${counter.getAndIncrement()}"}"
}

override fun createSpatial(): CameraSpatial = DetachedHeadCameraSpatial(this)


override fun createPopulatesUBO(): PopulatesUBO = DetachedHeadCameraUBOPopulator(this)
open class DetachedHeadCameraUBOPopulator(override val cam: DetachedHeadCamera): Camera.CameraUBOPopulator(cam) {

Check warning on line 121 in src/main/kotlin/graphics/scenery/DetachedHeadCamera.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/DetachedHeadCamera.kt#L121

DetachedHeadCameraUBOPopulator is missing required documentation.
override fun populate(ubo: UBO) {
val camSpatial = cam.spatial()
val hmd = (cam.tracker as? Display)

(0 until cam.eyeCount).forEach { eye ->

Check warning on line 126 in src/main/kotlin/graphics/scenery/DetachedHeadCamera.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/DetachedHeadCamera.kt#L126

Using the forEach method on ranges has a heavy performance cost. Prefer using simple for loops.
ubo.add("projection$eye", {
(hmd?.getEyeProjection(eye, cam.nearPlaneDistance, cam.farPlaneDistance)
?: camSpatial.projection).applyVulkanCoordinateSystem()
})
ubo.add("inverseProjection$eye", {
(hmd?.getEyeProjection(eye, cam.nearPlaneDistance, cam.farPlaneDistance)
?: camSpatial.projection).applyVulkanCoordinateSystem().invert()
})
}
ubo.add("headShift", { hmd?.getHeadToEyeTransform(0) ?: Matrix4f().identity() })
ubo.add("IPD", { hmd?.getIPD() ?: 0.05f })
ubo.add("stereoEnabled", { cam.stereoEnabled })
}
}

var stereoEnabled = false
Copy link
Contributor

@smlpt smlpt Feb 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this false if it is a stereo camera?

internal set

class DetachedHeadCameraSpatial(private val cam: DetachedHeadCamera) : Camera.CameraSpatial(cam) {

override var projection: Matrix4f = Matrix4f().identity()
Expand Down
1 change: 0 additions & 1 deletion src/main/kotlin/graphics/scenery/Node.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import kotlin.collections.ArrayList

interface Node : Networkable {
var name: String
var nodeType: String
/** Children of the Node. */
var children: CopyOnWriteArrayList<Node>
/** Other nodes that have linked transforms. */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package graphics.scenery.attribute.populatesubo

import graphics.scenery.backends.UBO

open class DefaultPopulatesUBO: PopulatesUBO {

Check warning on line 5 in src/main/kotlin/graphics/scenery/attribute/populatesubo/DefaultPopulatesUBO.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/attribute/populatesubo/DefaultPopulatesUBO.kt#L5

DefaultPopulatesUBO is missing required documentation.
override fun populate(ubo: UBO) {

Check warning on line 6 in src/main/kotlin/graphics/scenery/attribute/populatesubo/DefaultPopulatesUBO.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/attribute/populatesubo/DefaultPopulatesUBO.kt#L6

This empty block of code can be removed.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package graphics.scenery.attribute.populatesubo

import graphics.scenery.Node

interface HasCustomPopulatesUBO<T: PopulatesUBO>: Node {

Check warning on line 5 in src/main/kotlin/graphics/scenery/attribute/populatesubo/HasCustomPopulatesUBO.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/attribute/populatesubo/HasCustomPopulatesUBO.kt#L5

HasCustomPopulatesUBO is missing required documentation.
fun createPopulatesUBO(): PopulatesUBO

Check warning on line 6 in src/main/kotlin/graphics/scenery/attribute/populatesubo/HasCustomPopulatesUBO.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/attribute/populatesubo/HasCustomPopulatesUBO.kt#L6

The function createPopulatesUBO is missing documentation.

fun addPopulatesUBO() {

Check warning on line 8 in src/main/kotlin/graphics/scenery/attribute/populatesubo/HasCustomPopulatesUBO.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/attribute/populatesubo/HasCustomPopulatesUBO.kt#L8

The function addPopulatesUBO is missing documentation.
addAttribute(PopulatesUBO::class.java, createPopulatesUBO())
}

fun populatesUBO(): T {

Check warning on line 12 in src/main/kotlin/graphics/scenery/attribute/populatesubo/HasCustomPopulatesUBO.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/attribute/populatesubo/HasCustomPopulatesUBO.kt#L12

The function populatesUBO is missing documentation.
return getAttribute(PopulatesUBO::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package graphics.scenery.attribute.populatesubo

interface HasPopulatesUBO: HasCustomPopulatesUBO<PopulatesUBO> {

Check warning on line 3 in src/main/kotlin/graphics/scenery/attribute/populatesubo/HasPopulatesUBO.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/attribute/populatesubo/HasPopulatesUBO.kt#L3

HasPopulatesUBO is missing required documentation.
override fun createPopulatesUBO(): PopulatesUBO {
return DefaultPopulatesUBO()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package graphics.scenery.attribute.populatesubo

import graphics.scenery.backends.UBO

interface PopulatesUBO {

Check warning on line 5 in src/main/kotlin/graphics/scenery/attribute/populatesubo/PopulatesUBO.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/attribute/populatesubo/PopulatesUBO.kt#L5

PopulatesUBO is missing required documentation.
fun populate(ubo: UBO)

Check warning on line 6 in src/main/kotlin/graphics/scenery/attribute/populatesubo/PopulatesUBO.kt

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/main/kotlin/graphics/scenery/attribute/populatesubo/PopulatesUBO.kt#L6

The function populate is missing documentation.
}
49 changes: 13 additions & 36 deletions src/main/kotlin/graphics/scenery/backends/vulkan/VulkanRenderer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import graphics.scenery.attribute.renderable.Renderable
import graphics.scenery.backends.*
import graphics.scenery.textures.Texture
import graphics.scenery.utils.*
import graphics.scenery.utils.extensions.applyVulkanCoordinateSystem
import kotlinx.coroutines.*
import org.joml.*
import org.lwjgl.PointerBuffer
Expand All @@ -28,7 +29,6 @@ import org.lwjgl.vulkan.KHRWin32Surface.VK_KHR_WIN32_SURFACE_EXTENSION_NAME
import org.lwjgl.vulkan.KHRXlibSurface.VK_KHR_XLIB_SURFACE_EXTENSION_NAME
import org.lwjgl.vulkan.MVKMacosSurface.VK_MVK_MACOS_SURFACE_EXTENSION_NAME
import org.lwjgl.vulkan.VK10.*
import java.awt.BorderLayout
import java.awt.image.BufferedImage
import java.awt.image.DataBufferByte
import java.io.File
Expand All @@ -39,7 +39,6 @@ import java.util.*
import java.util.concurrent.*
import java.util.concurrent.locks.ReentrantLock
import javax.imageio.ImageIO
import javax.swing.JFrame
import kotlin.concurrent.thread
import kotlin.concurrent.withLock
import kotlin.reflect.full.*
Expand Down Expand Up @@ -387,12 +386,6 @@ open class VulkanRenderer(hub: Hub,
private var renderConfig: RenderConfigReader.RenderConfig
private var flow: List<String> = listOf()

private val vulkanProjectionFix =
Matrix4f(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.5f, 0.0f,
0.0f, 0.0f, 0.5f, 1.0f)

final override var renderConfigFile: String = ""
set(config) {
Expand Down Expand Up @@ -1727,12 +1720,21 @@ open class VulkanRenderer(hub: Hub,

profiler?.end()

val cameraConfig = (0 until activeCamera.eyeCount).map { eye ->
VulkanRenderpass.CameraConfig(
view = Matrix4f(activeCamera.spatial().getTransformationForEye(eye)),
projection = Matrix4f(activeCamera.spatial().projection),
eye = eye
)
}.toMutableList()

flow.take(flow.size - 1).forEachIndexed { i, t ->
val si = submitInfo[i]
profiler?.begin("Renderer.$t")
logger.trace("Running pass {}", t)
val target = renderpasses[t]!!
val commandBuffer = target.commandBuffer
target.cameraConfiguration = cameraConfig

if (commandBuffer.submitted) {
commandBuffer.waitForFence()
Expand Down Expand Up @@ -1793,6 +1795,7 @@ open class VulkanRenderer(hub: Hub,
profiler?.begin("Renderer.${renderpasses.keys.last()}")
val viewportPass = renderpasses.values.last()
val viewportCommandBuffer = viewportPass.commandBuffer
viewportPass.cameraConfiguration = cameraConfig

logger.trace("Running viewport pass {}", renderpasses.keys.last())

Expand Down Expand Up @@ -2064,14 +2067,6 @@ open class VulkanRenderer(hub: Hub,
instanceMasters.isNotEmpty()
}

fun Matrix4f.applyVulkanCoordinateSystem(): Matrix4f {
val m = Matrix4f(vulkanProjectionFix)
m.mul(this)

return m
}


private fun getDescriptorCache(): TimestampedConcurrentHashMap<String, SimpleTimestamped<Long>> {
@Suppress("UNCHECKED_CAST")
return scene.metadata.getOrPut("DescriptorCache") {
Expand Down Expand Up @@ -2109,26 +2104,8 @@ open class VulkanRenderer(hub: Hub,

buffers.VRParameters.reset()
val vrUbo = defaultUBOs["VRParameters"]!!
vrUbo.add("projection0", {
(hmd?.getEyeProjection(0, cam.nearPlaneDistance, cam.farPlaneDistance)
?: camSpatial.projection).applyVulkanCoordinateSystem()
})
vrUbo.add("projection1", {
(hmd?.getEyeProjection(1, cam.nearPlaneDistance, cam.farPlaneDistance)
?: camSpatial.projection).applyVulkanCoordinateSystem()
})
vrUbo.add("inverseProjection0", {
(hmd?.getEyeProjection(0, cam.nearPlaneDistance, cam.farPlaneDistance)
?: camSpatial.projection).applyVulkanCoordinateSystem().invert()
})
vrUbo.add("inverseProjection1", {
(hmd?.getEyeProjection(1, cam.nearPlaneDistance, cam.farPlaneDistance)
?: camSpatial.projection).applyVulkanCoordinateSystem().invert()
})
vrUbo.add("headShift", { hmd?.getHeadToEyeTransform(0) ?: Matrix4f().identity() })
vrUbo.add("IPD", { hmd?.getIPD() ?: 0.05f })
vrUbo.add("stereoEnabled", { renderConfig.stereoEnabled.toInt() })

(cam as? DetachedHeadCamera)?.stereoEnabled = renderConfig.stereoEnabled
cam.populatesUBO().populate(vrUbo)
updated = vrUbo.populate()

buffers.UBOs.reset()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import graphics.scenery.attribute.material.Material
import graphics.scenery.backends.*
import graphics.scenery.utils.lazyLogger
import graphics.scenery.utils.RingBuffer
import org.joml.Matrix4f
import org.joml.Vector2f
import org.joml.Vector4f
import org.lwjgl.system.MemoryUtil.*
Expand Down Expand Up @@ -107,6 +108,24 @@ open class VulkanRenderpass(val name: String, var config: RenderConfigReader.Ren
/** Whether this renderpass will render to the viewport or to a [VulkanFramebuffer] */
var isViewportRenderpass = false

/** Data class for storing this passes' camera matrices. These are stored per-eye of the camera. */
data class CameraConfig(val view: Matrix4f, val projection: Matrix4f, val eye: Int)
/** A list of this passes' camera configuration, backed by a RingBuffer.
* For each element in the ring buffer, there will be multiple [CameraConfig]s stored,
* each corresponding to an eye of the camera at hand.
*/
var cameraConfiguration: MutableList<CameraConfig>
get() {
return cameraConfigurationBacking.get()
}

set(cc) {
cameraConfigurationBacking.put(cc)
}

private var cameraConfigurationBacking = RingBuffer(size = ringBufferSize,
default = { mutableListOf<CameraConfig>() })

/** The number of command buffers to keep in the [RingBuffer] [commandBufferBacking]. */
var commandBufferCount = 3
set(count) {
Expand Down
Loading
Loading