wip: improve render performance a bit

This commit is contained in:
Bixilon 2021-03-13 14:03:14 +01:00
parent 9cb2565135
commit 50d893f235
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
12 changed files with 111 additions and 71 deletions

View File

@ -43,7 +43,7 @@ object RenderConstants {
const val TEXT_LINE_PADDING = 0
const val CHUNK_SECTIONS_PER_MESH = 8
const val CHUNK_SECTIONS_PER_MESH = 16
const val MAXIMUM_CALLS_PER_FRAME = 20
const val MAXIMUM_CALLS_PER_FRAME = 10
}

View File

@ -224,7 +224,7 @@ class RenderWindow(
// Make the OpenGL context current
glfwMakeContextCurrent(windowId)
// Enable v-sync
glfwSwapInterval(1)
glfwSwapInterval(0)
// Make the window visible
@ -236,6 +236,8 @@ class RenderWindow(
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glEnable(GL_CULL_FACE)
textures.textures.add(Texture(TextureArray.DEBUG_TEXTURE))
@ -253,7 +255,6 @@ class RenderWindow(
textures.load()
worldRenderer.postInit()
hudRenderer.postInit()
@ -361,7 +362,6 @@ class RenderWindow(
camera.draw()
camera.handleInput(deltaFrameTime)
// handle opengl context tasks, but limit it per frame
var actionsDone = 0
for (renderQueueElement in renderQueue) {

View File

@ -29,9 +29,6 @@ import de.bixilon.minosoft.gui.rendering.textures.Texture
import de.bixilon.minosoft.protocol.network.Connection
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.logging.Log
import org.lwjgl.opengl.GL11.GL_CULL_FACE
import org.lwjgl.opengl.GL11.glEnable
import org.lwjgl.opengl.GL13.glDisable
import java.util.concurrent.ConcurrentHashMap
class WorldRenderer(
@ -40,8 +37,8 @@ class WorldRenderer(
val renderWindow: RenderWindow,
) : Renderer {
lateinit var chunkShader: Shader
val chunkSectionsToDraw = ConcurrentHashMap<ChunkPosition, ConcurrentHashMap<Int, SectionArrayMesh>>()
val visibleChunks: MutableSet<ChunkPosition> = mutableSetOf()
val allChunkSections = ConcurrentHashMap<ChunkPosition, ConcurrentHashMap<Int, SectionArrayMesh>>()
val visibleChunks = ConcurrentHashMap<ChunkPosition, ConcurrentHashMap<Int, SectionArrayMesh>>()
private lateinit var frustum: Frustum
private var currentTick = 0 // for animation usage
private var lastTickIncrementTime = 0L
@ -57,9 +54,6 @@ class WorldRenderer(
synchronized(this.queuedChunks) {
queuedChunks.remove(chunkPosition)
}
if (frustum.containsChunk(chunkPosition, connection)) {
visibleChunks.add(chunkPosition)
}
val chunk = world.getChunk(chunkPosition)!!
val dimensionSupports3dBiomes = connection.player.world.dimension?.supports3DBiomes ?: false
@ -126,8 +120,6 @@ class WorldRenderer(
}
override fun draw() {
glEnable(GL_CULL_FACE)
chunkShader.use()
if (Minosoft.getConfig().config.game.animations.textures) {
val currentTime = System.currentTimeMillis()
@ -137,15 +129,11 @@ class WorldRenderer(
}
}
for ((chunkLocation, map) in chunkSectionsToDraw) {
if (!visibleChunks.contains(chunkLocation)) {
continue
}
for ((_, map) in visibleChunks) {
for ((_, mesh) in map) {
mesh.draw()
}
}
glDisable(GL_CULL_FACE)
}
private fun resolveBlockTextureIds(blocks: Set<BlockState>): List<Texture> {
@ -195,15 +183,15 @@ class WorldRenderer(
synchronized(this.queuedChunks) {
queuedChunks.remove(chunkPosition)
}
chunkSectionsToDraw[chunkPosition] = ConcurrentHashMap()
allChunkSections[chunkPosition] = ConcurrentHashMap()
var currentChunks: MutableMap<Int, ChunkSection> = mutableMapOf()
var currentHeight = 0
var currentIndex = 0
for ((sectionHeight, section) in chunk.sections!!) {
if (sectionHeight / RenderConstants.CHUNK_SECTIONS_PER_MESH != currentHeight) {
if (getSectionIndex(sectionHeight) != currentIndex) {
prepareChunkSections(chunkPosition, currentChunks)
currentChunks = mutableMapOf()
currentHeight = sectionHeight / RenderConstants.CHUNK_SECTIONS_PER_MESH
currentIndex = getSectionIndex(sectionHeight)
}
currentChunks[sectionHeight] = section
}
@ -232,16 +220,23 @@ class WorldRenderer(
Minosoft.THREAD_POOL.execute {
val mesh = prepareSections(chunkPosition, sections)
var sectionMap = chunkSectionsToDraw[chunkPosition]
var sectionMap = allChunkSections[chunkPosition]
if (sectionMap == null) {
sectionMap = ConcurrentHashMap()
chunkSectionsToDraw[chunkPosition] = sectionMap
allChunkSections[chunkPosition] = sectionMap
}
if (frustum.containsChunk(chunkPosition, connection)) {
visibleChunks[chunkPosition] = sectionMap
}
mesh.preLoad()
renderWindow.renderQueue.add {
mesh.load()
meshes++
triangles += mesh.trianglesCount
val index = sections.iterator().next().key / RenderConstants.CHUNK_SECTIONS_PER_MESH // ToDo: Negative chunk locations
val index = getSectionIndex(sections.iterator().next().key)
sectionMap[index]?.let {
it.unload()
meshes--
@ -253,7 +248,7 @@ class WorldRenderer(
}
fun prepareChunkSection(chunkPosition: ChunkPosition, sectionHeight: Int) {
TODO()
// TODO()
}
fun clearChunkCache() {
@ -262,12 +257,14 @@ class WorldRenderer(
queuedChunks.clear()
}
renderWindow.renderQueue.add {
for ((location, map) in chunkSectionsToDraw) {
for ((location, map) in allChunkSections) {
for ((sectionHeight, mesh) in map) {
mesh.unload()
meshes--
triangles -= mesh.trianglesCount
map.remove(sectionHeight)
}
chunkSectionsToDraw.remove(location)
allChunkSections.remove(location)
}
}
}
@ -277,11 +274,13 @@ class WorldRenderer(
queuedChunks.remove(chunkPosition)
}
renderWindow.renderQueue.add {
chunkSectionsToDraw[chunkPosition]?.let {
allChunkSections[chunkPosition]?.let {
for ((_, mesh) in it) {
meshes--
triangles -= mesh.trianglesCount
mesh.unload()
}
chunkSectionsToDraw.remove(chunkPosition)
allChunkSections.remove(chunkPosition)
}
}
}
@ -300,12 +299,18 @@ class WorldRenderer(
fun recalculateFrustum(frustum: Frustum) {
visibleChunks.clear()
this.frustum = frustum
for ((chunkLocation, _) in chunkSectionsToDraw.entries) {
for ((chunkLocation, sectionMap) in allChunkSections.entries) {
if (frustum.containsChunk(chunkLocation, connection)) {
visibleChunks.add(chunkLocation)
visibleChunks[chunkLocation] = sectionMap
}
}
}
companion object {
fun getSectionIndex(sectionHeight: Int): Int {
return sectionHeight / RenderConstants.CHUNK_SECTIONS_PER_MESH // ToDo: Negative chunk locations
}
}
}
private operator fun Int.plus(upOrDown: Directions): Int {

View File

@ -34,12 +34,8 @@ import glm_.mat4x4.Mat4
import glm_.vec2.Vec2
class HUDRenderer(val connection: Connection, val renderWindow: RenderWindow) : Renderer {
val hudScale: HUDScale
get() {
return Minosoft.getConfig().config.game.hud.scale
}
private val temporaryToggledElements: MutableSet<HUDElement> = mutableSetOf()
private val hudElements: MutableMap<ResourceLocation, Pair<HUDElementProperties, HUDElement>> = mutableMapOf()
private val enabledHUDElement: MutableMap<ResourceLocation, Pair<HUDElementProperties, HUDElement>> = mutableMapOf()
private val hudShader = Shader(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "rendering/shader/hud_vertex.glsl"), ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "rendering/shader/hud_fragment.glsl"))
lateinit var hudAtlasElements: Map<ResourceLocation, HUDAtlasElement>
var orthographicMatrix: Mat4 = Mat4()
@ -105,19 +101,25 @@ class HUDRenderer(val connection: Connection, val renderWindow: RenderWindow) :
if (needToSafeConfig) {
Minosoft.getConfig().saveToFile()
}
hudElements[resourceLocation] = Pair(properties, hudElement)
val pair = Pair(properties, hudElement)
hudElements[resourceLocation] = pair
properties.toggleKeyBinding?.let {
// register key binding
renderWindow.registerKeyCallback(it) { _, _ ->
if (temporaryToggledElements.contains(hudElement)) {
temporaryToggledElements.remove(hudElement)
if (enabledHUDElement.contains(resourceLocation)) {
enabledHUDElement.remove(resourceLocation)
} else {
temporaryToggledElements.add(hudElement)
enabledHUDElement[resourceLocation] = pair
}
}
}
if (!properties.enabled) {
return
}
enabledHUDElement[resourceLocation] = pair
}
fun removeElement(resourceLocation: ResourceLocation) {
@ -148,27 +150,35 @@ class HUDRenderer(val connection: Connection, val renderWindow: RenderWindow) :
if (!hudEnabled) {
return
}
currentHUDMesh.unload()
currentHUDMesh = HUDMesh()
for ((elementProperties, hudElement) in hudElements.values) {
val toggled = temporaryToggledElements.contains(hudElement)
if (toggled && elementProperties.enabled || !toggled && !elementProperties.enabled) {
continue
var needsUpdate = false
val tempMesh = HUDMesh()
for ((_, hudElement) in enabledHUDElement.values) {
if (hudElement.layout.needsCacheUpdate()) {
needsUpdate = true
break
}
}
if (needsUpdate) {
for ((elementProperties, hudElement) in enabledHUDElement.values) {
hudElement.draw()
hudElement.draw()
val realScaleFactor = elementProperties.scale * hudScale.scale
val realSize = Vec2(hudElement.layout.fakeX ?: hudElement.layout.size.x, hudElement.layout.fakeY ?: hudElement.layout.size.y) * realScaleFactor
val realScaleFactor = elementProperties.scale * Minosoft.getConfig().config.game.hud.scale.scale
val realSize = Vec2(hudElement.layout.fakeX ?: hudElement.layout.size.x, hudElement.layout.fakeY ?: hudElement.layout.size.y) * realScaleFactor
val elementStart = getRealPosition(realSize, elementProperties, renderWindow.screenDimensions)
val elementStart = getRealPosition(realSize, elementProperties, renderWindow.screenDimensions)
hudElement.layout.checkCache(elementStart, realScaleFactor, orthographicMatrix, 0)
currentHUDMesh.addCacheMesh(hudElement.layout.cache)
hudElement.layout.checkCache(elementStart, realScaleFactor, orthographicMatrix, 0)
tempMesh.addCacheMesh(hudElement.layout.cache)
}
currentHUDMesh.unload()
tempMesh.preLoad()
tempMesh.load()
currentHUDMesh = tempMesh
}
hudShader.use()
currentHUDMesh.load()
currentHUDMesh.draw()
}

View File

@ -19,6 +19,7 @@ import de.bixilon.minosoft.gui.rendering.hud.HUDRenderer
import de.bixilon.minosoft.gui.rendering.hud.elements.HUDElement
import de.bixilon.minosoft.gui.rendering.hud.elements.primitive.TextElement
import de.bixilon.minosoft.modding.loading.ModLoader
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.GitInfo
import de.bixilon.minosoft.util.SystemInformation
import de.bixilon.minosoft.util.UnitFormatter
@ -36,7 +37,12 @@ class HUDSystemDebugElement(hudRenderer: HUDRenderer) : HUDElement(hudRenderer)
gpuVersionText = glGetString(GL_VERSION) ?: "unknown"
}
private var lastPrepareTime = 0L
override fun draw() {
if (System.currentTimeMillis() - lastPrepareTime < ProtocolDefinition.TICK_TIME) {
return
}
layout.clear()
for (text in listOf(
@ -64,6 +70,7 @@ class HUDSystemDebugElement(hudRenderer: HUDRenderer) : HUDElement(hudRenderer)
layout.addChild(textElement)
}
layout.pushChildrenToRight(1.0f)
lastPrepareTime = System.currentTimeMillis()
}
private fun getUsedMemory(): Long {

View File

@ -19,6 +19,7 @@ import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.hud.HUDRenderer
import de.bixilon.minosoft.gui.rendering.hud.elements.HUDElement
import de.bixilon.minosoft.gui.rendering.hud.elements.primitive.TextElement
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.UnitFormatter
import glm_.vec2.Vec2
@ -26,13 +27,19 @@ import glm_.vec2.Vec2
class HUDWorldDebugElement(hudRenderer: HUDRenderer) : HUDElement(hudRenderer) {
private val camera = hudRenderer.renderWindow.camera
private var lastPrepareTime = 0L
override fun draw() {
if (System.currentTimeMillis() - lastPrepareTime < ProtocolDefinition.TICK_TIME) {
return
}
layout.clear()
for (text in listOf(
"FPS: ${getFPS()}",
"Timings: avg ${getAvgFrameTime()}ms, min ${getMinFrameTime()}ms, max ${getMaxFrameTime()}ms",
"Chunks: q=${hudRenderer.renderWindow.worldRenderer.queuedChunks.size} v=${hudRenderer.renderWindow.worldRenderer.visibleChunks.size} p=${hudRenderer.renderWindow.worldRenderer.chunkSectionsToDraw.size} t=${hudRenderer.connection.player.world.chunks.size}",
"Chunks: q=${hudRenderer.renderWindow.worldRenderer.queuedChunks.size} v=${hudRenderer.renderWindow.worldRenderer.visibleChunks.size} p=${hudRenderer.renderWindow.worldRenderer.allChunkSections.size} t=${hudRenderer.connection.player.world.chunks.size}",
"GL: m=${UnitFormatter.formatNumber(hudRenderer.renderWindow.worldRenderer.meshes)} t=${UnitFormatter.formatNumber(hudRenderer.renderWindow.worldRenderer.triangles)}",
"Connected to ${hudRenderer.connection.address} on ${hudRenderer.connection.version} with ${hudRenderer.connection.player.account.username}",
"",
@ -56,6 +63,7 @@ class HUDWorldDebugElement(hudRenderer: HUDRenderer) : HUDElement(hudRenderer) {
val textElement = TextElement(ChatComponent.valueOf(text), hudRenderer.renderWindow.font, Vec2(2, layout.size.y + RenderConstants.TEXT_LINE_PADDING))
layout.addChild(textElement)
}
lastPrepareTime = System.currentTimeMillis()
}
private fun nanoToMillis1d(nanos: Long): String {

View File

@ -36,8 +36,12 @@ abstract class Element(
abstract fun recalculateSize()
fun needsCacheUpdate(): Boolean {
return cache.isEmpty()
}
fun checkCache(start: Vec2, scaleFactor: Float, matrix: Mat4, z: Int = 1) {
if (cache.isNotEmpty()) {
if (!needsCacheUpdate()) {
return
}
prepareCache(start, scaleFactor, matrix, z)

View File

@ -63,10 +63,10 @@ class ImageElement(
}
addVertex(Vec2(modelStart.x, modelStart.y), Vec2(uvStart.x, uvStart.y))
addVertex(Vec2(modelEnd.x, modelStart.y), Vec2(uvEnd.x, uvStart.y))
addVertex(Vec2(modelStart.x, modelEnd.y), Vec2(uvStart.x, uvEnd.y))
addVertex(Vec2(modelStart.x, modelEnd.y), Vec2(uvStart.x, uvEnd.y))
addVertex(Vec2(modelEnd.x, modelStart.y), Vec2(uvEnd.x, uvStart.y))
addVertex(Vec2(modelStart.x, modelEnd.y), Vec2(uvStart.x, uvEnd.y))
addVertex(Vec2(modelEnd.x, modelEnd.y), Vec2(uvEnd.x, uvEnd.y))
addVertex(Vec2(modelEnd.x, modelStart.y), Vec2(uvEnd.x, uvStart.y))
}
}

View File

@ -19,7 +19,6 @@ import org.lwjgl.opengl.GL12.glTexImage3D
import org.lwjgl.opengl.GL12.glTexSubImage3D
import org.lwjgl.opengl.GL13.*
import org.lwjgl.opengl.GL30.GL_TEXTURE_2D_ARRAY
import org.lwjgl.opengl.GL30.glGenerateMipmap
import java.nio.ByteBuffer
class TextureArray(val textures: MutableList<Texture>) {
@ -68,7 +67,7 @@ class TextureArray(val textures: MutableList<Texture>) {
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, texture.layer, texture.width, texture.height, 1, GL_RGBA, GL_UNSIGNED_BYTE, texture.buffer)
texture.buffer.clear()
}
glGenerateMipmap(GL_TEXTURE_2D_ARRAY)
// glGenerateMipmap(GL_TEXTURE_2D_ARRAY)
return textureId
}

View File

@ -23,18 +23,27 @@ abstract class Mesh {
private var vbo: Int = 0
var trianglesCount: Int = 0
private set
private var rawData: FloatArray? = null
open fun preLoad() {
rawData = data.toFloatArray()
data.clear()
}
abstract fun load()
protected fun initializeBuffers(floatsPerVertex: Int) {
trianglesCount = data.size / floatsPerVertex
check(rawData != null) { "Mesh was not pre initialized!" }
trianglesCount = rawData!!.size / floatsPerVertex
vao = glGenVertexArrays()
vbo = glGenBuffers()
glBindVertexArray(vao)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, data.toFloatArray(), GL_STATIC_DRAW)
data.clear() // clear data ((do not store in memory)
glBufferData(GL_ARRAY_BUFFER, rawData!!, GL_STATIC_DRAW)
rawData = null
}
protected fun unbind() {

View File

@ -41,6 +41,6 @@ void main() {
return;
}
// passTextureCoordinates = vec3(0,0,0);
// passTextureCoordinates = vec3(textureIndex, textureLayer);
passTextureCoordinates = vec3(textureIndex.x, textureIndex.y + (animatedTextureData.z * ((animationTick / int(animatedTextureData.x)) % int(animatedTextureData.y))), textureLayer);
}

View File

@ -20,8 +20,6 @@ in vec4 passTintColor;
uniform sampler2DArray textureArray;
// #define DEBUG
void main() {
vec4 texelColor = texture(textureArray, passTextureCoordinates);
@ -40,7 +38,7 @@ void main() {
outColor = texelColor;
}
#ifdef DEBUG
outColor = vec4(1.0f, 0.0f, 0.5f, 1.0f);
#endif
//
// outColor = vec4(1.0f, 0.0f, 0.5f, 1.0f);
//
}