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

Input ports may be declared as abstract GLSL functions #352

Merged
merged 14 commits into from
Mar 18, 2021
Merged
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
83 changes: 61 additions & 22 deletions src/commonMain/kotlin/baaahs/gl/glsl/GlslCode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ class GlslCode(
fun toGlsl(
namespace: Namespace,
symbolsToNamespace: Set<String>,
symbolMap: Map<String, String>
symbolMap: Map<String, GlslExpr>
): String {
return "${lineNumber?.let { "\n#line $lineNumber\n" }}" +
replaceCodeWords(fullText) {
symbolMap[it]
symbolMap[it]?.s
?: if (it == name || symbolsToNamespace.contains(it)) {
namespace.qualify(it)
} else {
Expand Down Expand Up @@ -146,14 +146,14 @@ class GlslCode(
val type: GlslType
val isVarying: Boolean
val isGlobalInput: Boolean
val isAbstractFunction: Boolean
val hint: Hint?
val lineNumber: Int?

fun toInputPort(plugins: Plugins, parent: GlslFunction?): InputPort {
val contentTypeFromPlugin = try {
hint?.pluginRef
?.let { plugins.findDataSourceBuilder(it) }
?.contentType
?.let { plugins.findDataSourceBuilder(it).contentType }
} catch (e: Exception) {
null
}
Expand All @@ -167,12 +167,15 @@ class GlslCode(
title = title,
pluginRef = hint?.pluginRef,
pluginConfig = hint?.config,
glslArgSite = this
glslArgSite = this,
injectedData = findInjectedData(plugins)
)
}

fun findContentType(plugins: Plugins, parent: GlslFunction?) =
hint?.contentType(plugins)
hint?.contentType("type", plugins)

fun findInjectedData(plugins: Plugins): Map<String, ContentType> = emptyMap()
}

data class GlslVar(
Expand All @@ -187,6 +190,7 @@ class GlslCode(
) : GlslStatement, GlslArgSite {
override val title get() = name.englishize()
override val isGlobalInput: Boolean get() = isUniform || isVarying
override val isAbstractFunction: Boolean get() = false
override val hint: Hint? by lazy { Hint.parse(comments.joinToString(" ") { it.trim() }, lineNumber) }

override fun stripSource() = copy(fullText = "", lineNumber = null)
Expand Down Expand Up @@ -257,26 +261,56 @@ class GlslCode(
val params: List<GlslParam>,
override val fullText: String,
override val lineNumber: Int? = null,
override val comments: List<String> = emptyList()
) : GlslStatement {
val hint: Hint? by lazy { Hint.from(comments, lineNumber) }
override val comments: List<String> = emptyList(),
val isAbstract: Boolean = false
) : GlslStatement, GlslArgSite {
override val title: String get() = name.englishize()
override val type: GlslType get() = returnType
override val isVarying: Boolean get() = true
override val isGlobalInput: Boolean get() = false
override val isAbstractFunction: Boolean get() = isAbstract

override val hint: Hint? by lazy { Hint.from(comments, lineNumber) }

override fun stripSource() = copy(lineNumber = null)

fun invocationGlsl(namespace: Namespace, resultVar: String, portMap: Map<String, String>): String {
val assignment = if (returnType != GlslType.Void) {
"$resultVar = "
} else ""

val args = params.joinToString(", ") { glslParam ->
if (glslParam.isOut)
resultVar
else
portMap[glslParam.name]
?: "/* huh? ${glslParam.name} */"
}
override fun findContentType(plugins: Plugins, parent: GlslFunction?): ContentType? {
return hint?.contentType("return", plugins)
?: super.findContentType(plugins, parent)
}

return assignment + namespace.qualify(name) + "($args)"
override fun findInjectedData(plugins: Plugins): Map<String, ContentType> {
return params.associate { it.name to (it.findContentType(plugins, this) ?: ContentType.Unknown) }
}

override fun toGlsl(
namespace: Namespace,
symbolsToNamespace: Set<String>,
symbolMap: Map<String, GlslExpr>
): String {
// Chomp trailing ';' if it's an abstract method.
return super.toGlsl(namespace, symbolsToNamespace, symbolMap)
.let { if (isAbstract) it.trimEnd(';') else it }
}

fun invoker(namespace: Namespace, portMap: Map<String, GlslExpr>): Invoker {
return object : Invoker {
override fun toGlsl(resultVar: String): String {
val assignment = if (returnType != GlslType.Void) {
"$resultVar = "
} else ""

val args = params.joinToString(", ") { glslParam ->
if (glslParam.isOut)
resultVar
else
portMap[glslParam.name]?.s
?: "/* huh? ${glslParam.name} */"
}

return assignment + namespace.qualify(name) + "($args)"
}
}
}
}

Expand All @@ -292,6 +326,7 @@ class GlslCode(
override val isVarying: Boolean get() = true
override val isGlobalInput: Boolean get() = false
override val hint: Hint? by lazy { Hint.from(comments, lineNumber) }
override val isAbstractFunction: Boolean get() = false

override fun findContentType(plugins: Plugins, parent: GlslFunction?): ContentType? {
return super.findContentType(plugins, parent)
Expand All @@ -312,4 +347,8 @@ class GlslCode(
}
}
}

interface Invoker {
fun toGlsl(resultVar: String): String
}
}
3 changes: 3 additions & 0 deletions src/commonMain/kotlin/baaahs/gl/glsl/GlslExpr.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package baaahs.gl.glsl

data class GlslExpr(val s: String)
8 changes: 7 additions & 1 deletion src/commonMain/kotlin/baaahs/gl/glsl/GlslParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ class GlslParser {
val returnType: GlslType
val name: String
val params = arrayListOf<GlslCode.GlslParam>()
var isAbstract = true

init {
if (tokensSoFar.size != 2)
Expand All @@ -385,8 +386,13 @@ class GlslParser {
name = tokensSoFar[1]
}

override fun visitLeftCurlyBrace(): ParseState {
isAbstract = false
return super.visitLeftCurlyBrace()
}

override fun createStatement(): GlslCode.GlslStatement =
GlslCode.GlslFunction(name, returnType, params, textAsString, lineNumber, comments)
GlslCode.GlslFunction(name, returnType, params, textAsString, lineNumber, comments, isAbstract)

inner class Params(
recipientOfNextComment: Statement? = null
Expand Down
21 changes: 13 additions & 8 deletions src/commonMain/kotlin/baaahs/gl/glsl/GlslType.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package baaahs.gl.glsl

import baaahs.show.mutable.MutableConstPort
import baaahs.show.mutable.MutablePort

sealed class GlslType constructor(
val glslLiteral: String,
val defaultInitializer: String = "$glslLiteral(0.)"
val defaultInitializer: GlslExpr = GlslExpr("$glslLiteral(0.)")
) {
init {
@Suppress("LeakingThis")
types[glslLiteral] = this
}

val mutableDefaultInitializer: MutablePort get() = MutableConstPort(defaultInitializer.s, this)

private class OtherGlslType(glslLiteral: String) : GlslType(glslLiteral)
class Struct(
val name: String,
val fields: Map<String, GlslType>,
defaultInitializer: String = initializerFor(fields)
defaultInitializer: GlslExpr = initializerFor(fields)
) : GlslType(name, defaultInitializer) {
constructor(glslStruct: GlslCode.GlslStruct)
: this(glslStruct.name, glslStruct.fields)
Expand All @@ -26,7 +31,7 @@ sealed class GlslType constructor(
constructor(
name: String,
vararg fields: Pair<String, GlslType>,
defaultInitializer: String
defaultInitializer: GlslExpr
) : this(name, mapOf(*fields), defaultInitializer)

fun toGlsl(namespace: GlslCode.Namespace?, publicStructNames: Set<String>): String {
Expand Down Expand Up @@ -63,24 +68,24 @@ sealed class GlslType constructor(


companion object {
private fun initializerFor(fields: Map<String, GlslType>): String =
private fun initializerFor(fields: Map<String, GlslType>): GlslExpr =
StringBuilder().apply {
append("{ ")
fields.entries.forEachIndexed { index, (_, glslType) ->
if (index > 0)
append(", ")
append(glslType.defaultInitializer)
append(glslType.defaultInitializer.s)
}
append(" }")
}.toString()
}.toString().let { GlslExpr(it) }
}
}

object Float : GlslType("float", "0.")
object Float : GlslType("float", GlslExpr("0."))
object Vec2 : GlslType("vec2")
object Vec3 : GlslType("vec3")
object Vec4 : GlslType("vec4")
object Int : GlslType("int", "0")
object Int : GlslType("int", GlslExpr("0"))
object Sampler2D : GlslType("sampler2D")
object Void : GlslType("void")

Expand Down
7 changes: 5 additions & 2 deletions src/commonMain/kotlin/baaahs/gl/patch/Component.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package baaahs.gl.patch

import baaahs.gl.glsl.GlslExpr
import baaahs.gl.glsl.GlslType

interface Component {
val title: String
val outputVar: String?
val resultType: GlslType

val invokeFromMain: Boolean

fun appendStructs(buf: StringBuilder)
fun appendDeclarations(buf: StringBuilder)
fun appendInvokeAndSet(buf: StringBuilder, prefix: String)
fun appendInvokeAndSet(buf: StringBuilder, injectionParams: Map<String, ContentType> = emptyMap())

fun getExpression(): String
fun getExpression(prefix: String): GlslExpr
}
30 changes: 24 additions & 6 deletions src/commonMain/kotlin/baaahs/gl/patch/ContentType.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package baaahs.gl.patch

import baaahs.gl.glsl.GlslCode
import baaahs.gl.glsl.GlslExpr
import baaahs.gl.glsl.GlslType
import baaahs.plugin.core.MovingHeadParams

Expand All @@ -10,12 +11,12 @@ class ContentType(
val glslType: GlslType,
/** If false, this content type won't be suggested for matching GLSL types, it must be explicitly specified. */
val suggest: Boolean = true,
private val typeAdaptations: Map<GlslType, (String) -> String> = emptyMap(),
private val typeAdaptations: Map<GlslType, (GlslExpr) -> GlslExpr> = emptyMap(),
/** If [glslType] is a [GlslType.Struct], we currently need to provide a hint for converting it to a vector. */
val outputRepresentation: GlslType = glslType,
private val defaultInitializer: ((GlslType) -> String)? = null
private val defaultInitializer: ((GlslType) -> GlslExpr)? = null
) {
fun initializer(dataType: GlslType): String =
fun initializer(dataType: GlslType): GlslExpr =
defaultInitializer?.invoke(dataType)
?: officialDefaultInitializer(dataType)

Expand All @@ -26,7 +27,7 @@ class ContentType(
private fun officialDefaultInitializer(dataType: GlslType) =
(if (dataType == glslType) glslType else dataType).defaultInitializer

fun adapt(expression: String, toType: GlslType): String {
fun adapt(expression: GlslExpr, toType: GlslType): GlslExpr {
return typeAdaptations[toType]?.invoke(expression)
?: expression
}
Expand All @@ -35,6 +36,23 @@ class ContentType(
return id == "unknown" || id.startsWith("unknown/")
}

/**
* OpenGL doesn't support struct buffers directly, so emit any struct results to scalar arrays.
*/
fun appendResultAsScalars(buf: StringBuilder, varName: String) {
if (outputRepresentation != glslType) {
// Pass struct members through an output-friendly type.
buf.append(outputRepresentation.glslLiteral, "(")
(glslType as GlslType.Struct).fields.entries.forEachIndexed { index, (name, _) ->
if (index > 0) buf.append(",")
buf.append("\n $varName.$name")
}
buf.append("\n )")
} else {
buf.append(varName)
}
}

override fun toString(): String = "ContentType($id [$glslType])"

override fun equals(other: Any?): Boolean {
Expand Down Expand Up @@ -70,14 +88,14 @@ class ContentType(

val UvCoordinate = ContentType(
"uv-coordinate", "U/V Coordinate", GlslType.Vec2,
typeAdaptations = mapOf(GlslType.Vec4 to { "$it.xy" })
typeAdaptations = mapOf(GlslType.Vec4 to { GlslExpr("${it.s}.xy") })
)
val XyCoordinate = ContentType("xy-coordinate", "X/Y Coordinate", GlslType.Vec2)
val ModelInfo = ContentType("model-info", "Model Info", MoreTypes.ModelInfo.glslType)
val Mouse = ContentType("mouse", "Mouse", GlslType.Vec2)
val XyzCoordinate = ContentType("xyz-coordinate", "X/Y/Z Coordinate", GlslType.Vec3)
val Color = ContentType("color", "Color", GlslType.Vec4) { type ->
if (type == GlslType.Vec4) "vec4(0., 0., 0., 1.)" else type.defaultInitializer
if (type == GlslType.Vec4) GlslExpr("vec4(0., 0., 0., 1.)") else type.defaultInitializer
}
val Time = ContentType("time", "Time", GlslType.Float)
val Float = ContentType("float", "Float", GlslType.Float)
Expand Down
11 changes: 7 additions & 4 deletions src/commonMain/kotlin/baaahs/gl/patch/DataSourceComponent.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package baaahs.gl.patch

import baaahs.gl.glsl.GlslExpr
import baaahs.gl.glsl.GlslType
import baaahs.show.DataSource

Expand All @@ -10,6 +11,8 @@ class DataSourceComponent(val dataSource: DataSource, val varName: String) : Com
get() = null
override val resultType: GlslType
get() = dataSource.getType()
override val invokeFromMain: Boolean
get() = true

override fun appendStructs(buf: StringBuilder) {
val glslType = dataSource.contentType.glslType
Expand All @@ -26,11 +29,11 @@ class DataSourceComponent(val dataSource: DataSource, val varName: String) : Com
}
}

override fun appendInvokeAndSet(buf: StringBuilder, prefix: String) {
dataSource.appendInvokeAndSet(buf, prefix, varName)
override fun appendInvokeAndSet(buf: StringBuilder, injectionParams: Map<String, ContentType>) {
dataSource.appendInvokeAndSet(buf, varName)
}

override fun getExpression(): String {
return dataSource.getVarName(varName)
override fun getExpression(prefix: String): GlslExpr {
return GlslExpr(dataSource.getVarName(varName))
}
}
Loading