|
|
@@ -11,11 +11,10 @@ import androidx.compose.ui.scene.ComposeScene
|
|
|
|
import androidx.compose.ui.scene.MultiLayerComposeScene
|
|
|
|
import androidx.compose.ui.scene.MultiLayerComposeScene
|
|
|
|
import androidx.compose.ui.unit.Density
|
|
|
|
import androidx.compose.ui.unit.Density
|
|
|
|
import androidx.compose.ui.unit.IntSize
|
|
|
|
import androidx.compose.ui.unit.IntSize
|
|
|
|
import com.mojang.blaze3d.platform.GlStateManager
|
|
|
|
|
|
|
|
import com.mojang.blaze3d.platform.TextureUtil
|
|
|
|
import com.mojang.blaze3d.platform.TextureUtil
|
|
|
|
import com.mojang.blaze3d.systems.RenderSystem
|
|
|
|
import com.mojang.blaze3d.systems.RenderSystem
|
|
|
|
import kotlinx.coroutines.CoroutineDispatcher
|
|
|
|
import kotlinx.atomicfu.atomic
|
|
|
|
import kotlinx.coroutines.Runnable
|
|
|
|
import kotlinx.coroutines.*
|
|
|
|
import net.minecraft.client.MinecraftClient
|
|
|
|
import net.minecraft.client.MinecraftClient
|
|
|
|
import net.minecraft.client.gui.DrawContext
|
|
|
|
import net.minecraft.client.gui.DrawContext
|
|
|
|
import net.minecraft.client.gui.screen.Screen
|
|
|
|
import net.minecraft.client.gui.screen.Screen
|
|
|
@@ -27,118 +26,117 @@ import net.minecraft.text.Text
|
|
|
|
import org.jetbrains.skia.*
|
|
|
|
import org.jetbrains.skia.*
|
|
|
|
import org.jetbrains.skiko.FrameDispatcher
|
|
|
|
import org.jetbrains.skiko.FrameDispatcher
|
|
|
|
import org.lwjgl.glfw.GLFW.*
|
|
|
|
import org.lwjgl.glfw.GLFW.*
|
|
|
|
|
|
|
|
import org.lwjgl.opengl.ARBSync.glClientWaitSync
|
|
|
|
|
|
|
|
import org.lwjgl.opengl.ARBSync.glDeleteSync
|
|
|
|
|
|
|
|
import org.lwjgl.opengl.ARBSync.glFenceSync
|
|
|
|
|
|
|
|
import org.lwjgl.opengl.GL
|
|
|
|
import org.lwjgl.opengl.GL30.*
|
|
|
|
import org.lwjgl.opengl.GL30.*
|
|
|
|
|
|
|
|
import org.lwjgl.opengl.GL32.GL_SYNC_FLUSH_COMMANDS_BIT
|
|
|
|
|
|
|
|
import org.lwjgl.opengl.GL32C.GL_SYNC_GPU_COMMANDS_COMPLETE
|
|
|
|
import org.lwjgl.system.MemoryUtil.NULL
|
|
|
|
import org.lwjgl.system.MemoryUtil.NULL
|
|
|
|
import kotlin.coroutines.CoroutineContext
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@OptIn(InternalComposeUiApi::class)
|
|
|
|
@OptIn(InternalComposeUiApi::class)
|
|
|
|
class ComposeScreen(
|
|
|
|
class ComposeScreen(
|
|
|
|
val composeContent: @Composable (ComposeScreen) -> Unit
|
|
|
|
val composeContent: @Composable (ComposeScreen) -> Unit
|
|
|
|
) : Screen(Text.literal("FrameRenderComposeScreen")) {
|
|
|
|
) : Screen(Text.literal("FrameRenderComposeScreen")) {
|
|
|
|
companion object {
|
|
|
|
|
|
|
|
private val frameBufferID by lazy { GlStateManager.glGenFramebuffers() }
|
|
|
|
|
|
|
|
private val textureID by lazy { TextureUtil.generateTextureId() }
|
|
|
|
|
|
|
|
private val composeLayer: RenderLayer by lazy {
|
|
|
|
|
|
|
|
RenderLayer.of(
|
|
|
|
|
|
|
|
"cpu_render_compose",
|
|
|
|
|
|
|
|
VertexFormats.POSITION_TEXTURE,
|
|
|
|
|
|
|
|
VertexFormat.DrawMode.QUADS,
|
|
|
|
|
|
|
|
786432,
|
|
|
|
|
|
|
|
RenderLayer.MultiPhaseParameters.builder()
|
|
|
|
|
|
|
|
.texture(RenderPhase.TextureBase({
|
|
|
|
|
|
|
|
RenderSystem.setShaderTexture(0, textureID)
|
|
|
|
|
|
|
|
}, {}))
|
|
|
|
|
|
|
|
.program(RenderPhase.POSITION_TEXTURE_PROGRAM)
|
|
|
|
|
|
|
|
.transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY)
|
|
|
|
|
|
|
|
.depthTest(RenderPhase.LEQUAL_DEPTH_TEST)
|
|
|
|
|
|
|
|
.build(false)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private val windowID by lazy {
|
|
|
|
|
|
|
|
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE)
|
|
|
|
|
|
|
|
val oldWindow = glfwGetCurrentContext()
|
|
|
|
|
|
|
|
glfwCreateWindow(1, 1, "", NULL, oldWindow)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private val windowWidth: Int get() = MinecraftClient.getInstance().window.width
|
|
|
|
private val windowWidth: Int get() = MinecraftClient.getInstance().window.width
|
|
|
|
private val windowHeight: Int get() = MinecraftClient.getInstance().window.height
|
|
|
|
private val windowHeight: Int get() = MinecraftClient.getInstance().window.height
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private var frameBufferID = -1
|
|
|
|
|
|
|
|
private val textureID = TextureUtil.generateTextureId()
|
|
|
|
|
|
|
|
private val composeLayer: RenderLayer by lazy {
|
|
|
|
|
|
|
|
RenderLayer.of(
|
|
|
|
|
|
|
|
"cpu_render_compose",
|
|
|
|
|
|
|
|
VertexFormats.POSITION_TEXTURE,
|
|
|
|
|
|
|
|
VertexFormat.DrawMode.QUADS,
|
|
|
|
|
|
|
|
786432,
|
|
|
|
|
|
|
|
RenderLayer.MultiPhaseParameters.builder()
|
|
|
|
|
|
|
|
.texture(RenderPhase.TextureBase({
|
|
|
|
|
|
|
|
RenderSystem.setShaderTexture(0, textureID)
|
|
|
|
|
|
|
|
}, {}))
|
|
|
|
|
|
|
|
.program(RenderPhase.POSITION_TEXTURE_PROGRAM)
|
|
|
|
|
|
|
|
.transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY)
|
|
|
|
|
|
|
|
.depthTest(RenderPhase.LEQUAL_DEPTH_TEST)
|
|
|
|
|
|
|
|
.build(false)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private var windowID: Long = -1
|
|
|
|
private lateinit var directContext: DirectContext
|
|
|
|
private lateinit var directContext: DirectContext
|
|
|
|
private lateinit var surface: Surface
|
|
|
|
private lateinit var surface: Surface
|
|
|
|
private lateinit var canvas: Canvas
|
|
|
|
private lateinit var canvas: Canvas
|
|
|
|
private lateinit var scene: ComposeScene
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private val tasks = mutableListOf<Runnable>()
|
|
|
|
private val mainScope = MainScope()
|
|
|
|
private val tasksCopy = mutableListOf<Runnable>()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private val coroutineDispatcher = object : CoroutineDispatcher() {
|
|
|
|
private val fenceSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)
|
|
|
|
override fun dispatch(context: CoroutineContext, block: Runnable) {
|
|
|
|
private val needRedraw = atomic(true)
|
|
|
|
synchronized(tasks) {
|
|
|
|
private val frameDispatcher = FrameDispatcher(mainScope) {
|
|
|
|
tasks.add(block)
|
|
|
|
needRedraw.value = true
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private val frameDispatcher = FrameDispatcher(coroutineDispatcher) {
|
|
|
|
private val scene: ComposeScene = MultiLayerComposeScene(
|
|
|
|
RenderSystem.recordRenderCall {
|
|
|
|
density = Density(MinecraftClient.getInstance().options.guiScale.value.toFloat() / 2),
|
|
|
|
if (surface.isClosed) {
|
|
|
|
size = IntSize(windowWidth, windowHeight),
|
|
|
|
return@recordRenderCall
|
|
|
|
coroutineContext = Dispatchers.Main,
|
|
|
|
}
|
|
|
|
invalidate = { frameDispatcher.scheduleFrame() }
|
|
|
|
val oldWindowID = glfwGetCurrentContext()
|
|
|
|
).apply {
|
|
|
|
glfwMakeContextCurrent(windowID)
|
|
|
|
setContent {
|
|
|
|
|
|
|
|
composeContent(this@ComposeScreen)
|
|
|
|
surface.canvas.clear(Color.TRANSPARENT)
|
|
|
|
|
|
|
|
scene.render(canvas, System.nanoTime())
|
|
|
|
|
|
|
|
directContext.flush()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
glfwMakeContextCurrent(oldWindowID)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override fun init() {
|
|
|
|
override fun init() {
|
|
|
|
val oldFrameBuffer = glGetInteger(GL_FRAMEBUFFER_BINDING)
|
|
|
|
val oldWindow = glfwGetCurrentContext()
|
|
|
|
val oldTexture = glGetInteger(GL_TEXTURE_BINDING_2D)
|
|
|
|
glfwMakeContextCurrent(0)
|
|
|
|
|
|
|
|
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE)
|
|
|
|
|
|
|
|
windowID = glfwCreateWindow(1, 1, "", NULL, oldWindow)
|
|
|
|
|
|
|
|
val initTask = mainScope.async {
|
|
|
|
|
|
|
|
glfwMakeContextCurrent(windowID)
|
|
|
|
|
|
|
|
GL.createCapabilities()
|
|
|
|
|
|
|
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferID)
|
|
|
|
frameBufferID = glGenFramebuffers()
|
|
|
|
glBindTexture(GL_TEXTURE_2D, textureID)
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, frameBufferID)
|
|
|
|
glTexImage2D(
|
|
|
|
glBindTexture(GL_TEXTURE_2D, textureID)
|
|
|
|
GL_TEXTURE_2D, 0, GL_RGBA, windowWidth, windowHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, windowWidth, windowHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)
|
|
|
|
)
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0)
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, oldFrameBuffer)
|
|
|
|
directContext = DirectContext.makeGL()
|
|
|
|
glBindTexture(GL_TEXTURE_2D, oldTexture)
|
|
|
|
val renderTarget = BackendRenderTarget.makeGL(
|
|
|
|
|
|
|
|
windowWidth, windowHeight, 0, 8, frameBufferID, GL_RGBA8
|
|
|
|
directContext = DirectContext.makeGL()
|
|
|
|
)
|
|
|
|
val renderTarget = BackendRenderTarget.makeGL(
|
|
|
|
surface = Surface.makeFromBackendRenderTarget(
|
|
|
|
windowWidth, windowHeight, 0, 8, frameBufferID, GL_RGBA8
|
|
|
|
directContext, renderTarget, SurfaceOrigin.TOP_LEFT, SurfaceColorFormat.RGBA_8888, ColorSpace.sRGB
|
|
|
|
)
|
|
|
|
) ?: throw IllegalStateException("Surface could not be created")
|
|
|
|
surface = Surface.makeFromBackendRenderTarget(
|
|
|
|
canvas = surface.canvas.asComposeCanvas()
|
|
|
|
directContext, renderTarget, SurfaceOrigin.TOP_LEFT, SurfaceColorFormat.RGBA_8888, ColorSpace.sRGB
|
|
|
|
glfwMakeContextCurrent(0)
|
|
|
|
)!!
|
|
|
|
|
|
|
|
canvas = surface.canvas.asComposeCanvas()
|
|
|
|
|
|
|
|
scene = MultiLayerComposeScene(
|
|
|
|
|
|
|
|
density = Density(MinecraftClient.getInstance().options.guiScale.value.toFloat() / 2),
|
|
|
|
|
|
|
|
size = IntSize(windowWidth, windowHeight),
|
|
|
|
|
|
|
|
coroutineContext = coroutineDispatcher,
|
|
|
|
|
|
|
|
invalidate = { frameDispatcher.scheduleFrame() }
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
scene.setContent {
|
|
|
|
|
|
|
|
composeContent(this@ComposeScreen)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
super.init()
|
|
|
|
runBlocking {
|
|
|
|
|
|
|
|
initTask.await()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
glfwMakeContextCurrent(oldWindow)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
|
|
|
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
|
|
|
synchronized(tasks) {
|
|
|
|
super.render(context, mouseX, mouseY, delta)
|
|
|
|
tasksCopy.clear()
|
|
|
|
if (needRedraw.compareAndSet(true, false)) {
|
|
|
|
tasksCopy.addAll(tasks)
|
|
|
|
val oldWindow = glfwGetCurrentContext()
|
|
|
|
tasks.clear()
|
|
|
|
glfwMakeContextCurrent(0)
|
|
|
|
}
|
|
|
|
val renderTask = mainScope.async {
|
|
|
|
for (runnable in tasksCopy) {
|
|
|
|
if (surface.isClosed) {
|
|
|
|
runnable.run()
|
|
|
|
return@async
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
glfwMakeContextCurrent(windowID)
|
|
|
|
|
|
|
|
surface.canvas.clear(Color.TRANSPARENT)
|
|
|
|
|
|
|
|
scene.render(canvas, System.nanoTime())
|
|
|
|
|
|
|
|
directContext.flush()
|
|
|
|
|
|
|
|
glfwMakeContextCurrent(0)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
runBlocking {
|
|
|
|
|
|
|
|
renderTask.await()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
glfwMakeContextCurrent(oldWindow)
|
|
|
|
|
|
|
|
glClientWaitSync(fenceSync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000000)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
context.draw {
|
|
|
|
context.draw {
|
|
|
|
val vertexConsumer = it.getBuffer(composeLayer)
|
|
|
|
val vertexConsumer = it.getBuffer(composeLayer)
|
|
|
@@ -163,7 +161,7 @@ class ComposeScreen(
|
|
|
|
override fun mouseMoved(mouseX: Double, mouseY: Double) {
|
|
|
|
override fun mouseMoved(mouseX: Double, mouseY: Double) {
|
|
|
|
val x = MinecraftClient.getInstance().mouse.x.toFloat()
|
|
|
|
val x = MinecraftClient.getInstance().mouse.x.toFloat()
|
|
|
|
val y = MinecraftClient.getInstance().mouse.y.toFloat()
|
|
|
|
val y = MinecraftClient.getInstance().mouse.y.toFloat()
|
|
|
|
coroutineDispatcher.dispatch(coroutineDispatcher) {
|
|
|
|
mainScope.launch {
|
|
|
|
scene.sendPointerEvent(
|
|
|
|
scene.sendPointerEvent(
|
|
|
|
PointerEventType.Move,
|
|
|
|
PointerEventType.Move,
|
|
|
|
position = Offset(x, y)
|
|
|
|
position = Offset(x, y)
|
|
|
@@ -172,7 +170,7 @@ class ComposeScreen(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
|
|
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
|
|
coroutineDispatcher.dispatch(coroutineDispatcher) {
|
|
|
|
mainScope.launch {
|
|
|
|
val x = MinecraftClient.getInstance().mouse.x.toFloat()
|
|
|
|
val x = MinecraftClient.getInstance().mouse.x.toFloat()
|
|
|
|
val y = MinecraftClient.getInstance().mouse.y.toFloat()
|
|
|
|
val y = MinecraftClient.getInstance().mouse.y.toFloat()
|
|
|
|
scene.sendPointerEvent(
|
|
|
|
scene.sendPointerEvent(
|
|
|
@@ -185,7 +183,7 @@ class ComposeScreen(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
|
|
override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
|
|
|
coroutineDispatcher.dispatch(coroutineDispatcher) {
|
|
|
|
mainScope.launch {
|
|
|
|
val x = MinecraftClient.getInstance().mouse.x.toFloat()
|
|
|
|
val x = MinecraftClient.getInstance().mouse.x.toFloat()
|
|
|
|
val y = MinecraftClient.getInstance().mouse.y.toFloat()
|
|
|
|
val y = MinecraftClient.getInstance().mouse.y.toFloat()
|
|
|
|
scene.sendPointerEvent(
|
|
|
|
scene.sendPointerEvent(
|
|
|
@@ -203,7 +201,7 @@ class ComposeScreen(
|
|
|
|
val x = MinecraftClient.getInstance().mouse.x.toFloat()
|
|
|
|
val x = MinecraftClient.getInstance().mouse.x.toFloat()
|
|
|
|
val y = MinecraftClient.getInstance().mouse.y.toFloat()
|
|
|
|
val y = MinecraftClient.getInstance().mouse.y.toFloat()
|
|
|
|
println("mouseScrolled: $x $y $horizontalAmount $verticalAmount")
|
|
|
|
println("mouseScrolled: $x $y $horizontalAmount $verticalAmount")
|
|
|
|
coroutineDispatcher.dispatch(coroutineDispatcher) {
|
|
|
|
mainScope.launch {
|
|
|
|
scene.sendPointerEvent(
|
|
|
|
scene.sendPointerEvent(
|
|
|
|
PointerEventType.Scroll,
|
|
|
|
PointerEventType.Scroll,
|
|
|
|
button = PointerButton.Tertiary,
|
|
|
|
button = PointerButton.Tertiary,
|
|
|
@@ -216,8 +214,17 @@ class ComposeScreen(
|
|
|
|
|
|
|
|
|
|
|
|
override fun close() {
|
|
|
|
override fun close() {
|
|
|
|
MinecraftClient.getInstance().setScreen(null)
|
|
|
|
MinecraftClient.getInstance().setScreen(null)
|
|
|
|
|
|
|
|
if (textureID != 0) {
|
|
|
|
|
|
|
|
glDeleteTextures(textureID)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frameBufferID != -1) {
|
|
|
|
|
|
|
|
glDeleteFramebuffers(frameBufferID)
|
|
|
|
|
|
|
|
frameBufferID = -1
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
glDeleteSync(fenceSync)
|
|
|
|
|
|
|
|
directContext.close()
|
|
|
|
surface.close()
|
|
|
|
surface.close()
|
|
|
|
scene.close()
|
|
|
|
scene.close()
|
|
|
|
directContext.close()
|
|
|
|
mainScope.cancel()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|