Skip to content

Commit

Permalink
WIP — fix bug #366 etc.
Browse files Browse the repository at this point in the history
  • Loading branch information
xian committed May 1, 2021
1 parent ec97780 commit e9dd43f
Show file tree
Hide file tree
Showing 13 changed files with 185 additions and 81 deletions.
45 changes: 26 additions & 19 deletions src/commonMain/kotlin/baaahs/gl/glsl/GlslCode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ class GlslCode(
}
}

fun interface Substitutions {
fun substitute(word: String): String
}

interface GlslStatement {
val name: String
val fullText: String
Expand All @@ -93,20 +97,13 @@ class GlslCode(

fun stripSource(): GlslStatement

fun toGlsl(
namespace: Namespace,
symbolsToNamespace: Set<String>,
symbolMap: Map<String, GlslExpr>
): String {
fun toGlsl(substitutions: Substitutions): String {
return substituteGlsl(fullText, substitutions, lineNumber)
}

fun substituteGlsl(text: String, substitutions: Substitutions, lineNumber: Int?): String {
return "${lineNumber?.let { "\n#line $lineNumber\n" }}" +
replaceCodeWords(fullText) {
symbolMap[it]?.s
?: if (it == name || symbolsToNamespace.contains(it)) {
namespace.qualify(it)
} else {
it
}
}
replaceCodeWords(text) { substitutions.substitute(it) }
}
}

Expand Down Expand Up @@ -159,15 +156,29 @@ class GlslCode(
val isConst: Boolean = false,
val isUniform: Boolean = false,
override val isVarying: Boolean = false,
val initExpr: String? = null,
override val lineNumber: Int? = null,
override val comments: List<String> = emptyList()
) : 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) }
val deferInitialization: Boolean = !isConst && initExpr != null

override fun stripSource() = copy(fullText = "", lineNumber = null)

fun declarationToGlsl(substitutions: Substitutions): String {
val declaration = if (deferInitialization) {
fullText.substring(0, fullText.indexOf(initExpr!!)) + ";"
} else fullText
return substituteGlsl(declaration, substitutions, lineNumber)
}

fun assignmentToGlsl(substitutions: Substitutions): String {
val assignment = " $name$initExpr;"
return substituteGlsl(assignment, substitutions, lineNumber)
}
}

class Hint(
Expand Down Expand Up @@ -252,13 +263,9 @@ class GlslCode(
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 {
override fun toGlsl(substitutions: Substitutions): String {
// Chomp trailing ';' if it's an abstract method.
return super.toGlsl(namespace, symbolsToNamespace, symbolMap)
return super.toGlsl(substitutions)
.let { if (isAbstract) it.trimEnd(';') else it }
}

Expand Down
4 changes: 2 additions & 2 deletions src/commonMain/kotlin/baaahs/gl/glsl/GlslParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ class GlslParser {

return Regex("(?:(const|uniform|varying)\\s+)?(\\w+)\\s+(\\w+)(\\s*\\[\\s*\\d+\\s*])?(\\s*=.*)?;", RegexOption.MULTILINE)
.find(text.trim())?.let {
val (qualifier, type, name, arraySpec, constValue) = it.destructured
val (qualifier, type, name, arraySpec, initExpr) = it.destructured
var (isConst, isUniform, isVarying) = arrayOf(false, false, false)
when (qualifier) {
"const" -> isConst = true
Expand All @@ -277,7 +277,7 @@ class GlslParser {
val (trimmedText, trimmedLineNumber) = chomp(text, lineNumber)
GlslCode.GlslVar(
name, context.findType(type), trimmedText, isConst, isUniform, isVarying,
trimmedLineNumber, comments
if (initExpr.isEmpty()) null else initExpr, trimmedLineNumber, comments
)
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/commonMain/kotlin/baaahs/gl/patch/Component.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ interface Component {
fun appendInvokeAndSet(buf: StringBuilder, injectionParams: Map<String, ContentType> = emptyMap())

fun getExpression(prefix: String): GlslExpr

fun getInit(): String? = null
}
4 changes: 4 additions & 0 deletions src/commonMain/kotlin/baaahs/gl/patch/LinkedPatch.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class LinkedPatch(
buf.append("\n#line 10001\n")
buf.append("void main() {\n")

components.forEach { component ->
component.getInit()?.let { buf.append(it) }
}

components.filter { it.invokeFromMain }.forEach { component ->
component.appendInvokeAndSet(buf)
}
Expand Down
19 changes: 17 additions & 2 deletions src/commonMain/kotlin/baaahs/gl/patch/ShaderComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import baaahs.gl.glsl.GlslCode
import baaahs.gl.glsl.GlslExpr
import baaahs.gl.glsl.GlslType
import baaahs.gl.shader.InputPort
import baaahs.gl.shader.ShaderSubstitutions
import baaahs.show.live.LinkedShaderInstance

class ShaderComponent(
Expand Down Expand Up @@ -58,6 +59,8 @@ class ShaderComponent(
private val resolvedPortMap get() =
portMap + mapOf(shaderInstance.shader.outputPort.id to GlslExpr(outputVar))

private val substitutions = ShaderSubstitutions(shaderInstance.shader, namespace, resolvedPortMap)

override fun appendStructs(buf: StringBuilder) {
val openShader = shaderInstance.shader
val portStructs = openShader.portStructs
Expand All @@ -81,7 +84,7 @@ class ShaderComponent(

appendInjectionCode(buf)

buf.append(openShader.toGlsl(namespace, resolvedPortMap), "\n")
buf.append(openShader.toGlsl(substitutions), "\n")
}

private fun appendInjectionCode(buf: StringBuilder) {
Expand Down Expand Up @@ -114,7 +117,9 @@ class ShaderComponent(
link: ProgramNode
) {
val fn = inputPort.glslArgSite as GlslCode.GlslFunction
buf.append(fn.toGlsl(namespace, emptySet(), emptyMap()))
buf.append(fn.toGlsl {
if (it == fn.name) namespace.qualify(it) else it
})
buf.append(" {\n")

val destComponent = findUpstreamComponent(link)
Expand Down Expand Up @@ -148,6 +153,16 @@ class ShaderComponent(
}.let { GlslExpr(it) }
}

override fun getInit(): String? {
return if (shaderInstance.shader.requiresInit) {
StringBuilder().apply {
append(" // Init ${title}.\n")
append(" ${ShaderSubstitutions.namespacedInitFnName(namespace)}();\n")
append("\n")
}.toString()
} else null
}

override fun toString(): String = "ShaderComponent(${prefix}_$id)"

}
83 changes: 58 additions & 25 deletions src/commonMain/kotlin/baaahs/gl/shader/OpenShader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import baaahs.gl.shader.type.ShaderType
import baaahs.only
import baaahs.show.Shader
import baaahs.unknown
import kotlin.collections.set

interface OpenShader : RefCounted {
val shader: Shader
val src: String get() = glslCode.src
val glslCode: GlslCode
val title: String
val requiresInit: Boolean get() = false
val entryPoint: GlslFunction

val inputPorts: List<InputPort>
Expand All @@ -38,10 +38,7 @@ interface OpenShader : RefCounted {
(inputPorts.map { it.contentType.glslType } + outputPort.contentType.glslType)
.filterIsInstance<GlslType.Struct>()

/** The list of global variables that aren't also backing input ports. */
val globalVars: List<GlslCode.GlslVar>

fun toGlsl(namespace: Namespace, portMap: Map<String, GlslExpr> = emptyMap()): String
fun toGlsl(substitutions: GlslCode.Substitutions): String

fun invoker(
namespace: Namespace,
Expand All @@ -67,34 +64,29 @@ interface OpenShader : RefCounted {

override val title: String get() = shader.title

override val globalVars: List<GlslCode.GlslVar> get() =
glslCode.globalVars.filter { !it.isUniform && !it.isVarying }
override val requiresInit = globalVars.any { it.deferInitialization }

override fun toGlsl(
namespace: Namespace,
portMap: Map<String, GlslExpr>
): String {
override fun toGlsl(substitutions: GlslCode.Substitutions): String {
val buf = StringBuilder()

val nonUniformGlobalsMap = hashMapOf<String, GlslExpr>()
globalVars.forEach { glslVar ->
nonUniformGlobalsMap[glslVar.name] = GlslExpr(namespace.qualify(glslVar.name))
buf.append(glslVar.toGlsl(namespace, glslCode.symbolNames, emptyMap()))
buf.append(glslVar.declarationToGlsl(substitutions))
buf.append("\n")
}

val uniformGlobalsMap = portMap.filter { (id, _) ->
val inputPort = findInputPortOrNull(id)

inputPort?.isGlobal == true ||
(outputPort.id == id && !outputPort.isParam)
glslCode.functions.filterNot { it.isAbstract }.forEach { glslFunction ->
buf.append(glslFunction.toGlsl(substitutions))
buf.append("\n")
}

val symbolsToNamespace = glslCode.symbolNames.toSet() - portStructs.map { it.name}
val symbolMap = uniformGlobalsMap + nonUniformGlobalsMap
glslCode.functions.filterNot { it.isAbstract }.forEach { glslFunction ->
buf.append(glslFunction.toGlsl(namespace, symbolsToNamespace, symbolMap))
if (requiresInit) {
buf.append("\n")
buf.append("void ${substitutions.substitute(ShaderSubstitutions.initFnName)}() {")
globalVars.forEach {
if (it.deferInitialization) {
buf.append(" ${it.assignmentToGlsl(substitutions)};\n")
}
}
buf.append("}\n")
}

return buf.toString()
Expand All @@ -113,4 +105,45 @@ interface OpenShader : RefCounted {
override fun hashCode(): Int =
src.hashCode()
}
}
}

class ShaderSubstitutions(
val openShader: OpenShader,
val namespace: Namespace,
portMap: Map<String, GlslExpr>
) : GlslCode.Substitutions {
private val uniformGlobalsMap = portMap.filter { (id, _) ->
val inputPort = openShader.findInputPortOrNull(id)
inputPort?.isGlobal == true ||
(openShader.outputPort.id == id && !openShader.outputPort.isParam)
}

private val nonUniformGlobalsMap = openShader.globalVars.associate { glslVar ->
glslVar.name to GlslExpr(namespace.qualify(glslVar.name))
}

private val symbolsToNamespace =
openShader.glslCode.symbolNames.toSet() - openShader.portStructs.map { it.name }

private val specialSymbols =
mapOf(initFnName to GlslExpr(namespacedInitFnName(namespace)))

private val symbolMap = uniformGlobalsMap + nonUniformGlobalsMap + specialSymbols

override fun substitute(word: String): String =
symbolMap[word]?.s
?: if (symbolsToNamespace.contains(word)) {
namespace.qualify(word)
} else {
word
}

companion object {
val initFnName = "_init_"
fun namespacedInitFnName(namespace: Namespace) = namespace.internalQualify("init")
}
}

/** The list of global variables that aren't also backing input ports. */
private val OpenShader.globalVars: List<GlslCode.GlslVar> get() =
glslCode.globalVars.filter { !it.isUniform && !it.isVarying }
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,14 @@ abstract class BaseShaderDialect(id: String) : ShaderDialect(id) {
open fun findWellKnownInputPorts(glslCode: GlslCode, declaredInputPorts: Set<String>): List<InputPort> {
if (wellKnownInputPorts.isEmpty()) return emptyList()

val iVars = glslCode.statements.flatMap { glslFunction ->
val symbolsMentionedInShader = glslCode.statements.flatMap { glslFunction ->
Regex("\\w+").findAll(glslFunction.fullText).map { it.value }.filter { word ->
wellKnownInputPortsById.containsKey(word)
}.toList()
}.toSet()

return wellKnownInputPorts.filter { inputPort ->
iVars.contains(inputPort.id) && !declaredInputPorts.contains(inputPort.id)
symbolsMentionedInShader.contains(inputPort.id) && !declaredInputPorts.contains(inputPort.id)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,9 @@ object IsfShaderDialect : BaseShaderDialect("baaahs.Core:ISF") {
}

private fun findIsfShaderDeclaration(glslCode: GlslCode): IsfShader? {
val match = Regex("^/\\*(\\{[\\s\\S]*})\\*/").find(glslCode.src)
?: return null

val (jsonDecl) = match.destructured
if (!glslCode.src.startsWith("/*{")) return null
val endOfJson = glslCode.src.indexOf("*/")
val jsonDecl = glslCode.src.substring(2, endOfJson)
try {
return json.decodeFromString(IsfShader.serializer(), jsonDecl)
} catch (e: SerializationException) {
Expand Down
4 changes: 3 additions & 1 deletion src/commonTest/kotlin/baaahs/gl/glsl/GlslCodeSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ object GlslCodeSpec : Spek({

context("const") {
override(text) { "const int i = 3;" }
expectValue(GlslCode.GlslVar("i", GlslType.Int, "const int i = 3;", isConst = true)) { variable }
expectValue(
GlslCode.GlslVar("i", GlslType.Int, "const int i = 3;", isConst = true, initExpr = " = 3")
) { variable }
}

context("uniform") {
Expand Down
5 changes: 4 additions & 1 deletion src/commonTest/kotlin/baaahs/gl/glsl/GlslParserSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,9 @@ object GlslParserSpec : Spek({
it("handles nested macro expansions") {
val glslFunction = glslCode.functions.only()

val glsl = glslFunction.toGlsl(Namespace("ns"), emptySet(), emptyMap())
val glsl = glslFunction.toGlsl { text ->
if (text == "main") Namespace("ns").qualify(text) else text
}

expect(glsl.trim())
.toBe(
Expand Down Expand Up @@ -423,6 +425,7 @@ object GlslParserSpec : Spek({
GlslVar(
"baseColor", GlslType.Vec3, "const vec3 baseColor = vec3(0.0,0.09,0.18);",
isConst = true,
initExpr = " = vec3(0.0,0.09,0.18)",
lineNumber = 1
)
)
Expand Down
Loading

0 comments on commit e9dd43f

Please sign in to comment.