mirror of
https://github.com/GTNewHorizons/Angelica.git
synced 2026-11-13 17:04:15 +00:00
Fix GLES crashing at startup and rendering black (#2346)
This commit is contained in:
1 parent
a6d014a9eb
commit
3876218405
24 files changed
+668
-185
No files matched your search
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- name: Install a software Vulkan driver
|
- name: Install a software Vulkan driver
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -y
|
sudo apt-get update -y
|
||||||
sudo apt-get install -y xvfb mesa-vulkan-drivers
|
sudo apt-get install -y xvfb mesa-vulkan-drivers libgl1-mesa-dri
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-java@v5
|
- uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
25
|
25
|
||||||
- uses: gradle/actions/setup-gradle@v5
|
- uses: gradle/actions/setup-gradle@v5
|
||||||
- name: Run SDL-GPU tests
|
- name: Run SDL-GPU tests
|
||||||
run: xvfb-run -a --server-args="-screen 0 1366x768x24" ./gradlew --build-cache --stacktrace --continue :sdl-gpu:glsmSdlTest :sdl-gpu:sdlGpuDeviceTest
|
run: xvfb-run -a --server-args="-screen 0 1366x768x24" ./gradlew --build-cache --stacktrace --continue :sdl-gpu:glsmSdlTest :sdl-gpu:sdlGpuDeviceTest :lwjgl3-backend:glesTest
|
||||||
- name: Check LWJGL did not extract natives to the shared temp dir
|
- name: Check LWJGL did not extract natives to the shared temp dir
|
||||||
run: test ! -e "/tmp/lwjgl_$USER"
|
run: test ! -e "/tmp/lwjgl_$USER"
|
||||||
- name: Attach SDL-GPU test results
|
- name: Attach SDL-GPU test results
|
||||||
@@ -48,3 +48,6 @@ jobs:
|
|||||||
sdl-gpu/build/test-results/
|
sdl-gpu/build/test-results/
|
||||||
sdl-gpu/build/reports/tests/
|
sdl-gpu/build/reports/tests/
|
||||||
sdl-gpu/hs_err_pid*.log
|
sdl-gpu/hs_err_pid*.log
|
||||||
|
lwjgl3-backend/build/test-results/
|
||||||
|
lwjgl3-backend/build/reports/tests/
|
||||||
|
lwjgl3-backend/hs_err_pid*.log
|
||||||
@@ -67,6 +67,22 @@ artifacts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LWJGL2-API stubs for test JVMs that strip the lwjgl-2 jars (glsmSdlTest, glesTest).
|
||||||
|
val lwjgl2StubsSourceSet = sourceSets.create("lwjgl2Stubs") {
|
||||||
|
java.srcDir("src/lwjgl2Stubs/java")
|
||||||
|
}
|
||||||
|
|
||||||
|
val lwjgl2Stubs by configurations.creating {
|
||||||
|
isCanBeConsumed = true
|
||||||
|
isCanBeResolved = false
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts {
|
||||||
|
add(lwjgl2Stubs.name, lwjgl2StubsSourceSet.java.classesDirectory) {
|
||||||
|
builtBy(tasks.named("lwjgl2StubsClasses"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
name = "Mojang"
|
name = "Mojang"
|
||||||
@@ -111,6 +127,7 @@ val spirvTestRuntime by configurations.creating {
|
|||||||
dependencies {
|
dependencies {
|
||||||
// MC compile stubs
|
// MC compile stubs
|
||||||
compileOnly(sourceSets["stubs"].output)
|
compileOnly(sourceSets["stubs"].output)
|
||||||
|
"lwjgl2StubsCompileOnly"(libs.lwjgl3.opengl)
|
||||||
|
|
||||||
// LWJGL
|
// LWJGL
|
||||||
compileOnly(libs.lwjgl2)
|
compileOnly(libs.lwjgl2)
|
||||||
|
|||||||
File renamed without changes.
+19
@@ -1,5 +1,7 @@
|
|||||||
package org.lwjgl.opengl;
|
package org.lwjgl.opengl;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
public class ContextCapabilities {
|
public class ContextCapabilities {
|
||||||
|
|
||||||
public boolean OpenGL30 = true;
|
public boolean OpenGL30 = true;
|
||||||
@@ -21,11 +23,28 @@ public class ContextCapabilities {
|
|||||||
public boolean GL_ARB_sampler_objects = true;
|
public boolean GL_ARB_sampler_objects = true;
|
||||||
public boolean GL_ARB_shader_image_load_store = true;
|
public boolean GL_ARB_shader_image_load_store = true;
|
||||||
public boolean GL_ARB_shader_storage_buffer_object = true;
|
public boolean GL_ARB_shader_storage_buffer_object = true;
|
||||||
|
public boolean GL_ARB_stencil_texturing = false;
|
||||||
public boolean GL_ARB_tessellation_shader = false;
|
public boolean GL_ARB_tessellation_shader = false;
|
||||||
|
public boolean GL_ARB_texture_storage = false;
|
||||||
|
|
||||||
public boolean GL_EXT_direct_state_access = false;
|
public boolean GL_EXT_direct_state_access = false;
|
||||||
public boolean GL_EXT_gpu_shader4 = true;
|
public boolean GL_EXT_gpu_shader4 = true;
|
||||||
public boolean GL_EXT_shader_image_load_store = true;
|
public boolean GL_EXT_shader_image_load_store = true;
|
||||||
|
|
||||||
public boolean GL_NVX_gpu_memory_info = false;
|
public boolean GL_NVX_gpu_memory_info = false;
|
||||||
|
|
||||||
|
public ContextCapabilities() {
|
||||||
|
if ("lwjgl3".equals(System.getProperty("glsm.stubs.capabilities"))) copyFromCurrentContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyFromCurrentContext() {
|
||||||
|
final GLCapabilities caps = GL.getCapabilities();
|
||||||
|
for (Field field : ContextCapabilities.class.getFields()) {
|
||||||
|
try {
|
||||||
|
field.setBoolean(this, GLCapabilities.class.getField(field.getName()).getBoolean(caps));
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new IllegalStateException("LWJGL3 GLCapabilities has no " + field.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
File renamed without changes.
@@ -0,0 +1,13 @@
|
|||||||
|
package org.lwjgl.opengl;
|
||||||
|
|
||||||
|
public final class GLContext {
|
||||||
|
|
||||||
|
private static ContextCapabilities capabilities;
|
||||||
|
|
||||||
|
private GLContext() {}
|
||||||
|
|
||||||
|
public static ContextCapabilities getCapabilities() {
|
||||||
|
if (capabilities == null) capabilities = new ContextCapabilities();
|
||||||
|
return capabilities;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -88,8 +88,11 @@ public final class GLSMRasterizer {
|
|||||||
static void probePointSizeRange(GLContextState glCtx) {
|
static void probePointSizeRange(GLContextState glCtx) {
|
||||||
final FloatBuffer range = glCtx.queryScratch;
|
final FloatBuffer range = glCtx.queryScratch;
|
||||||
range.clear();
|
range.clear();
|
||||||
RENDER_BACKEND.getFloat(GL11.GL_POINT_SIZE_RANGE, range);
|
float smooth = 0.0f;
|
||||||
final float smooth = range.get(1);
|
if (!RenderSystem.isGLES()) {
|
||||||
|
RENDER_BACKEND.getFloat(GL11.GL_POINT_SIZE_RANGE, range);
|
||||||
|
smooth = range.get(1);
|
||||||
|
}
|
||||||
RENDER_BACKEND.getFloat(GL12.GL_ALIASED_POINT_SIZE_RANGE, range);
|
RENDER_BACKEND.getFloat(GL12.GL_ALIASED_POINT_SIZE_RANGE, range);
|
||||||
maxPointSize = Math.max(smooth, range.get(1));
|
maxPointSize = Math.max(smooth, range.get(1));
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-4
@@ -15,8 +15,10 @@ import org.taumc.glsl.grammar.GLSLParser;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public final class GlslVulkanPreprocess {
|
public final class GlslVulkanPreprocess {
|
||||||
@@ -56,7 +58,9 @@ public final class GlslVulkanPreprocess {
|
|||||||
private GlslVulkanPreprocess() {}
|
private GlslVulkanPreprocess() {}
|
||||||
|
|
||||||
private record CacheKey(String source, int glShaderType, boolean separateReadOnlyImages) {}
|
private record CacheKey(String source, int glShaderType, boolean separateReadOnlyImages) {}
|
||||||
public record Result(String rewrittenSource, Set<String> boolUniforms, Set<String> explicitVsInputs) {}
|
public record Result(String rewrittenSource, Set<String> boolUniforms, Set<String> explicitVsInputs, Map<String, UniformBlock> uniformBlocks) {}
|
||||||
|
|
||||||
|
public record UniformBlock(String qualifierText, @Nullable String instanceName) {}
|
||||||
|
|
||||||
/** Replace {@code [startIdx, stopIdx]} with {@code replacement}. */
|
/** Replace {@code [startIdx, stopIdx]} with {@code replacement}. */
|
||||||
public record Edit(int startIdx, int stopIdx, String replacement) {}
|
public record Edit(int startIdx, int stopIdx, String replacement) {}
|
||||||
@@ -109,7 +113,7 @@ public final class GlslVulkanPreprocess {
|
|||||||
final List<Edit> edits = new ArrayList<>();
|
final List<Edit> edits = new ArrayList<>();
|
||||||
final Metadata meta = collectEdits(new ShaderIndex(root, source), glShaderType, debugName, separateReadOnlyImages, edits);
|
final Metadata meta = collectEdits(new ShaderIndex(root, source), glShaderType, debugName, separateReadOnlyImages, edits);
|
||||||
|
|
||||||
final Result out = new Result(applyEdits(source, edits), meta.boolUniforms(), meta.explicitVsInputs());
|
final Result out = new Result(applyEdits(source, edits), meta.boolUniforms(), meta.explicitVsInputs(), meta.uniformBlocks());
|
||||||
synchronized (CACHE) {
|
synchronized (CACHE) {
|
||||||
CACHE.putAndMoveToFirst(key, out);
|
CACHE.putAndMoveToFirst(key, out);
|
||||||
while (CACHE.size() > CACHE_MAX) CACHE.removeLast();
|
while (CACHE.size() > CACHE_MAX) CACHE.removeLast();
|
||||||
@@ -117,11 +121,12 @@ public final class GlslVulkanPreprocess {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Metadata(Set<String> boolUniforms, Set<String> explicitVsInputs, boolean needsSamplerless) {}
|
public record Metadata(Set<String> boolUniforms, Set<String> explicitVsInputs, boolean needsSamplerless, Map<String, UniformBlock> uniformBlocks) {}
|
||||||
|
|
||||||
public static Metadata collectEdits(ShaderIndex index, int glShaderType, String debugName, boolean separateReadOnlyImages, List<Edit> edits) {
|
public static Metadata collectEdits(ShaderIndex index, int glShaderType, String debugName, boolean separateReadOnlyImages, List<Edit> edits) {
|
||||||
final boolean isVertex = glShaderType == GL20.GL_VERTEX_SHADER;
|
final boolean isVertex = glShaderType == GL20.GL_VERTEX_SHADER;
|
||||||
final Set<String> bools = new HashSet<>();
|
final Set<String> bools = new HashSet<>();
|
||||||
|
Map<String, UniformBlock> uniformBlocks = Map.of();
|
||||||
final Set<String> explicitInputs = new HashSet<>();
|
final Set<String> explicitInputs = new HashSet<>();
|
||||||
final Set<String> readOnlyImages = new HashSet<>();
|
final Set<String> readOnlyImages = new HashSet<>();
|
||||||
boolean needsSamplerless = false;
|
boolean needsSamplerless = false;
|
||||||
@@ -130,6 +135,14 @@ public final class GlslVulkanPreprocess {
|
|||||||
final List<Integer> unlocatedVsInputStarts = new ArrayList<>();
|
final List<Integer> unlocatedVsInputStarts = new ArrayList<>();
|
||||||
|
|
||||||
for (GLSLParser.DeclarationContext declaration : index.globalDeclarations()) {
|
for (GLSLParser.DeclarationContext declaration : index.globalDeclarations()) {
|
||||||
|
if (declaration.LEFT_BRACE() != null && ShaderIndex.hasStorageQualifier(declaration.type_qualifier(), "uniform")) {
|
||||||
|
final GLSLParser.Type_qualifierContext blockQualifier = declaration.type_qualifier();
|
||||||
|
final String qualifierText = index.source().substring(startIdx(blockQualifier), stopIdx(blockQualifier) + 1);
|
||||||
|
final String instanceName = declaration.IDENTIFIER().size() > 1 ? declaration.IDENTIFIER(1).getText() : null;
|
||||||
|
if (uniformBlocks.isEmpty()) uniformBlocks = new HashMap<>();
|
||||||
|
uniformBlocks.put(declaration.IDENTIFIER(0).getText(), new UniformBlock(qualifierText, instanceName));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final GLSLParser.Init_declarator_listContext list = declaration.init_declarator_list();
|
final GLSLParser.Init_declarator_listContext list = declaration.init_declarator_list();
|
||||||
if (list == null) continue;
|
if (list == null) continue;
|
||||||
final GLSLParser.Single_declarationContext single = list.single_declaration();
|
final GLSLParser.Single_declarationContext single = list.single_declaration();
|
||||||
@@ -223,7 +236,7 @@ public final class GlslVulkanPreprocess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Metadata(bools, explicitInputs, needsSamplerless);
|
return new Metadata(bools, explicitInputs, needsSamplerless, uniformBlocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean renamesIdentifier(String name) {
|
public static boolean renamesIdentifier(String name) {
|
||||||
|
|||||||
+74
-37
@@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.shader;
|
|||||||
|
|
||||||
import com.gtnewhorizons.angelica.glsm.GlslTransformUtils;
|
import com.gtnewhorizons.angelica.glsm.GlslTransformUtils;
|
||||||
import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess.Edit;
|
import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess.Edit;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess.UniformBlock;
|
||||||
import me.eigenraven.lwjgl3ify.api.Lwjgl3Aware;
|
import me.eigenraven.lwjgl3ify.api.Lwjgl3Aware;
|
||||||
import org.antlr.v4.runtime.ParserRuleContext;
|
import org.antlr.v4.runtime.ParserRuleContext;
|
||||||
import org.antlr.v4.runtime.tree.ParseTree;
|
import org.antlr.v4.runtime.tree.ParseTree;
|
||||||
@@ -26,8 +27,10 @@ import org.taumc.glsl.grammar.GLSLParserBaseListener;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess.startIdx;
|
import static com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess.startIdx;
|
||||||
@@ -65,13 +68,13 @@ public final class SpirvShaderTranslator {
|
|||||||
try {
|
try {
|
||||||
final String emitted = emitGlslEs(cr.spirv(), debugName, glShaderType, pre.explicitVsInputs());
|
final String emitted = emitGlslEs(cr.spirv(), debugName, glShaderType, pre.explicitVsInputs());
|
||||||
if (emitted == null) return null;
|
if (emitted == null) return null;
|
||||||
return postProcessEsOutput(emitted, pre.boolUniforms());
|
return postProcessEsOutput(emitted, pre.boolUniforms(), pre.uniformBlocks());
|
||||||
} finally {
|
} finally {
|
||||||
memFree(cr.spirv());
|
memFree(cr.spirv());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @Nullable String postProcessEsOutput(String glslEs, Set<String> boolUniformNames) {
|
private static @Nullable String postProcessEsOutput(String glslEs, Set<String> boolUniformNames, Map<String, UniformBlock> uniformBlocks) {
|
||||||
final GLSLParser.Translation_unitContext root;
|
final GLSLParser.Translation_unitContext root;
|
||||||
try {
|
try {
|
||||||
root = GlslTransformUtils.parseFullQuiet(glslEs);
|
root = GlslTransformUtils.parseFullQuiet(glslEs);
|
||||||
@@ -80,21 +83,42 @@ public final class SpirvShaderTranslator {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final DefaultBlockInfo block = findDefaultBlock(root);
|
final BlockFinder finder = new BlockFinder(glslEs, uniformBlocks);
|
||||||
final List<Edit> edits = new ArrayList<>();
|
ParseTreeWalker.DEFAULT.walk(finder, root);
|
||||||
|
|
||||||
if (block != null) {
|
final List<Edit> edits = new ArrayList<>();
|
||||||
edits.add(new Edit(startIdx(block.structExternalDecl), stopIdx(block.structExternalDecl), buildLooseUniforms(block.members, boolUniformNames)));
|
BlockInfo defaultBlock = null;
|
||||||
|
final Map<String, Set<String>> bareMembersByVar = new HashMap<>();
|
||||||
|
for (BlockInfo block : finder.blocks) {
|
||||||
|
final String structReplacement;
|
||||||
|
if (block.source == null) {
|
||||||
|
defaultBlock = block;
|
||||||
|
structReplacement = buildLooseUniforms(block.members, boolUniformNames);
|
||||||
|
bareMembersByVar.put(block.varName, block.memberNames());
|
||||||
|
} else if (block.source.instanceName() == null) {
|
||||||
|
structReplacement = block.source.qualifierText() + " " + block.structName + " " + block.body + ";";
|
||||||
|
bareMembersByVar.put(block.varName, block.memberNames());
|
||||||
|
} else {
|
||||||
|
structReplacement = block.source.qualifierText() + " " + block.structName + " " + block.body + " " + block.declaratorText + ";";
|
||||||
|
}
|
||||||
|
edits.add(new Edit(startIdx(block.structExternalDecl), stopIdx(block.structExternalDecl), structReplacement));
|
||||||
edits.add(new Edit(startIdx(block.uniformExternalDecl), stopIdx(block.uniformExternalDecl), ""));
|
edits.add(new Edit(startIdx(block.uniformExternalDecl), stopIdx(block.uniformExternalDecl), ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
final UseSiteRewriter rewriter = new UseSiteRewriter(block, boolUniformNames, edits);
|
final Set<String> defaultMembers = defaultBlock == null ? Set.of() : bareMembersByVar.get(defaultBlock.varName);
|
||||||
|
final UseSiteRewriter rewriter = new UseSiteRewriter(defaultBlock, defaultMembers, boolUniformNames, bareMembersByVar, edits);
|
||||||
ParseTreeWalker.DEFAULT.walk(rewriter, root);
|
ParseTreeWalker.DEFAULT.walk(rewriter, root);
|
||||||
|
|
||||||
return GlslVulkanPreprocess.applyEdits(glslEs, edits);
|
return GlslVulkanPreprocess.applyEdits(glslEs, edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
private record DefaultBlockInfo(ParserRuleContext structExternalDecl, ParserRuleContext uniformExternalDecl, String blockVarName, List<MemberInfo> members) {}
|
private record BlockInfo(String structName, @Nullable UniformBlock source, ParserRuleContext structExternalDecl, String body, List<MemberInfo> members, ParserRuleContext uniformExternalDecl, String varName, String declaratorText) {
|
||||||
|
Set<String> memberNames() {
|
||||||
|
final Set<String> names = new HashSet<>();
|
||||||
|
for (MemberInfo m : members) names.add(m.name);
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private record MemberInfo(String typePrefix, String name) {}
|
private record MemberInfo(String typePrefix, String name) {}
|
||||||
|
|
||||||
@@ -111,31 +135,38 @@ public final class SpirvShaderTranslator {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @Nullable DefaultBlockInfo findDefaultBlock(GLSLParser.Translation_unitContext root) {
|
private static boolean isDefaultBlockStruct(String structName) {
|
||||||
final DefaultBlockFinder finder = new DefaultBlockFinder();
|
return structName.startsWith("_RESERVED_IDENTIFIER_FIXUP_") && structName.endsWith("DefaultUniformBlock");
|
||||||
ParseTreeWalker.DEFAULT.walk(finder, root);
|
|
||||||
if (finder.structExternalDecl == null || finder.uniformExternalDecl == null) return null;
|
|
||||||
return new DefaultBlockInfo(finder.structExternalDecl, finder.uniformExternalDecl, finder.blockVarName, finder.members);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class DefaultBlockFinder extends GLSLParserBaseListener {
|
private static final class BlockFinder extends GLSLParserBaseListener {
|
||||||
String structName;
|
private record PendingStruct(@Nullable UniformBlock source, ParserRuleContext externalDecl, String body, List<MemberInfo> members) {}
|
||||||
ParserRuleContext structExternalDecl;
|
|
||||||
ParserRuleContext uniformExternalDecl;
|
private final String glslEs;
|
||||||
String blockVarName;
|
private final Map<String, UniformBlock> sourceBlocks;
|
||||||
final List<MemberInfo> members = new ArrayList<>();
|
private final Map<String, PendingStruct> pending = new HashMap<>();
|
||||||
|
final List<BlockInfo> blocks = new ArrayList<>();
|
||||||
|
|
||||||
|
BlockFinder(String glslEs, Map<String, UniformBlock> sourceBlocks) {
|
||||||
|
this.glslEs = glslEs;
|
||||||
|
this.sourceBlocks = sourceBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enterStruct_specifier(GLSLParser.Struct_specifierContext ctx) {
|
public void enterStruct_specifier(GLSLParser.Struct_specifierContext ctx) {
|
||||||
if (structExternalDecl != null) return;
|
|
||||||
final TerminalNode id = ctx.IDENTIFIER();
|
final TerminalNode id = ctx.IDENTIFIER();
|
||||||
if (id == null) return;
|
if (id == null) return;
|
||||||
final String idText = id.getText();
|
final String idText = id.getText();
|
||||||
if (!idText.startsWith("_RESERVED_IDENTIFIER_FIXUP_") || !idText.endsWith("DefaultUniformBlock")) return;
|
final UniformBlock source = sourceBlocks.get(idText);
|
||||||
|
if (source == null && !isDefaultBlockStruct(idText)) return;
|
||||||
|
|
||||||
structName = idText;
|
final List<MemberInfo> members = source != null && source.instanceName() != null ? List.of() : collectMembers(ctx);
|
||||||
structExternalDecl = findExternalDeclaration(ctx);
|
final String body = glslEs.substring(ctx.LEFT_BRACE().getSymbol().getStartIndex(), ctx.RIGHT_BRACE().getSymbol().getStopIndex() + 1);
|
||||||
|
pending.putIfAbsent(idText, new PendingStruct(source, findExternalDeclaration(ctx), body, members));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<MemberInfo> collectMembers(GLSLParser.Struct_specifierContext ctx) {
|
||||||
|
final List<MemberInfo> members = new ArrayList<>();
|
||||||
for (GLSLParser.Struct_declarationContext sd : ctx.struct_declaration_list().struct_declaration()) {
|
for (GLSLParser.Struct_declarationContext sd : ctx.struct_declaration_list().struct_declaration()) {
|
||||||
final StringBuilder typePrefix = new StringBuilder();
|
final StringBuilder typePrefix = new StringBuilder();
|
||||||
if (sd.type_qualifier() != null) typePrefix.append(textWithSpaces(sd.type_qualifier())).append(' ');
|
if (sd.type_qualifier() != null) typePrefix.append(textWithSpaces(sd.type_qualifier())).append(' ');
|
||||||
@@ -146,11 +177,12 @@ public final class SpirvShaderTranslator {
|
|||||||
members.add(new MemberInfo(typePrefix + arraySuffix, decl.IDENTIFIER().getText()));
|
members.add(new MemberInfo(typePrefix + arraySuffix, decl.IDENTIFIER().getText()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return members;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enterInit_declarator_list(GLSLParser.Init_declarator_listContext ctx) {
|
public void enterInit_declarator_list(GLSLParser.Init_declarator_listContext ctx) {
|
||||||
if (structName == null || uniformExternalDecl != null) return;
|
if (pending.isEmpty()) return;
|
||||||
final GLSLParser.Single_declarationContext single = ctx.single_declaration();
|
final GLSLParser.Single_declarationContext single = ctx.single_declaration();
|
||||||
if (single == null || single.typeless_declaration() == null) return;
|
if (single == null || single.typeless_declaration() == null) return;
|
||||||
final GLSLParser.Fully_specified_typeContext fst = single.fully_specified_type();
|
final GLSLParser.Fully_specified_typeContext fst = single.fully_specified_type();
|
||||||
@@ -159,10 +191,14 @@ public final class SpirvShaderTranslator {
|
|||||||
if (fst.type_specifier() == null || fst.type_specifier().type_specifier_nonarray() == null) return;
|
if (fst.type_specifier() == null || fst.type_specifier().type_specifier_nonarray() == null) return;
|
||||||
final GLSLParser.Type_specifier_nonarrayContext ts = fst.type_specifier().type_specifier_nonarray();
|
final GLSLParser.Type_specifier_nonarrayContext ts = fst.type_specifier().type_specifier_nonarray();
|
||||||
if (ts.type_name() == null) return;
|
if (ts.type_name() == null) return;
|
||||||
if (!structName.equals(ts.type_name().getText())) return;
|
final String structName = ts.type_name().getText();
|
||||||
|
final PendingStruct struct = pending.remove(structName);
|
||||||
|
if (struct == null) return;
|
||||||
|
|
||||||
blockVarName = single.typeless_declaration().IDENTIFIER().getText();
|
final GLSLParser.Typeless_declarationContext declarator = single.typeless_declaration();
|
||||||
uniformExternalDecl = findExternalDeclaration(ctx);
|
final String declaratorText = glslEs.substring(startIdx(declarator), stopIdx(declarator) + 1);
|
||||||
|
blocks.add(new BlockInfo(structName, struct.source, struct.externalDecl, struct.body, struct.members,
|
||||||
|
findExternalDeclaration(ctx), declarator.IDENTIFIER().getText(), declaratorText));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String textWithSpaces(ParseTree tree) {
|
private static String textWithSpaces(ParseTree tree) {
|
||||||
@@ -192,17 +228,18 @@ public final class SpirvShaderTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final class UseSiteRewriter extends GLSLParserBaseListener {
|
private static final class UseSiteRewriter extends GLSLParserBaseListener {
|
||||||
private final @Nullable DefaultBlockInfo block;
|
private final @Nullable BlockInfo block;
|
||||||
private final Set<String> boolNames;
|
private final Set<String> boolNames;
|
||||||
|
private final Map<String, Set<String>> bareMembersByVar;
|
||||||
private final List<Edit> edits;
|
private final List<Edit> edits;
|
||||||
private final Set<String> memberNames;
|
private final Set<String> memberNames;
|
||||||
|
|
||||||
UseSiteRewriter(@Nullable DefaultBlockInfo block, Set<String> boolNames, List<Edit> edits) {
|
UseSiteRewriter(@Nullable BlockInfo block, Set<String> memberNames, Set<String> boolNames, Map<String, Set<String>> bareMembersByVar, List<Edit> edits) {
|
||||||
this.block = block;
|
this.block = block;
|
||||||
|
this.memberNames = memberNames;
|
||||||
this.boolNames = boolNames;
|
this.boolNames = boolNames;
|
||||||
|
this.bareMembersByVar = bareMembersByVar;
|
||||||
this.edits = edits;
|
this.edits = edits;
|
||||||
this.memberNames = new HashSet<>();
|
|
||||||
if (block != null) for (MemberInfo m : block.members) memberNames.add(m.name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -240,11 +277,11 @@ public final class SpirvShaderTranslator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block != null && ctx.getChildCount() == 3 && ctx.DOT() != null && ctx.field_selection() != null) {
|
if (ctx.getChildCount() == 3 && ctx.DOT() != null && ctx.field_selection() != null) {
|
||||||
final ParseTree inner = ctx.getChild(0);
|
final Set<String> bareMembers = bareMembersByVar.get(ctx.getChild(0).getText());
|
||||||
if (inner.getText().equals(block.blockVarName)) {
|
if (bareMembers != null) {
|
||||||
final String member = ctx.field_selection().getText();
|
final String member = ctx.field_selection().getText();
|
||||||
if (memberNames.contains(member) && !coveredByAncestorBoolCompare(ctx)) {
|
if (bareMembers.contains(member) && !coveredByAncestorBoolCompare(ctx)) {
|
||||||
edits.add(new Edit(startIdx(ctx), stopIdx(ctx), member));
|
edits.add(new Edit(startIdx(ctx), stopIdx(ctx), member));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -254,8 +291,8 @@ public final class SpirvShaderTranslator {
|
|||||||
private @Nullable String resolveBoolOperand(ParseTree operand) {
|
private @Nullable String resolveBoolOperand(ParseTree operand) {
|
||||||
final String text = operand.getText();
|
final String text = operand.getText();
|
||||||
if (boolNames.contains(text)) return text;
|
if (boolNames.contains(text)) return text;
|
||||||
if (block != null && text.startsWith(block.blockVarName + ".")) {
|
if (block != null && text.startsWith(block.varName + ".")) {
|
||||||
final String member = text.substring(block.blockVarName.length() + 1);
|
final String member = text.substring(block.varName.length() + 1);
|
||||||
if (boolNames.contains(member) && memberNames.contains(member)) return member;
|
if (boolNames.contains(member) && memberNames.contains(member)) return member;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package com.gtnewhorizons.angelica.glsm.texture;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
import org.lwjgl.opengl.GL12;
|
||||||
|
import org.lwjgl.opengl.GL13;
|
||||||
|
import org.lwjgl.opengl.GL30;
|
||||||
|
import org.lwjgl.opengl.GL32;
|
||||||
|
import org.lwjgl.opengl.GL40;
|
||||||
|
|
||||||
|
/** Emulates GL proxy textures for backends that lack them. */
|
||||||
|
public final class ProxyTexture {
|
||||||
|
|
||||||
|
private final int maxTextureSize;
|
||||||
|
private final int max3DTextureSize;
|
||||||
|
private final int maxCubeMapTextureSize;
|
||||||
|
private final int maxArrayTextureLayers;
|
||||||
|
|
||||||
|
private int target;
|
||||||
|
private int width;
|
||||||
|
private int height;
|
||||||
|
private int depth;
|
||||||
|
private int internalFormat;
|
||||||
|
|
||||||
|
public ProxyTexture(int maxTextureSize, int max3DTextureSize, int maxCubeMapTextureSize, int maxArrayTextureLayers) {
|
||||||
|
this.maxTextureSize = maxTextureSize;
|
||||||
|
this.max3DTextureSize = max3DTextureSize;
|
||||||
|
this.maxCubeMapTextureSize = maxCubeMapTextureSize;
|
||||||
|
this.maxArrayTextureLayers = maxArrayTextureLayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void record(int target, int level, int internalFormat, int width, int height, int depth, boolean formatSupported) {
|
||||||
|
final boolean layersInHeight = layersInHeight(target);
|
||||||
|
final boolean layersInDepth = layersInDepth(target);
|
||||||
|
final int dimLimit = dimensionLimit(target);
|
||||||
|
final int w = levelZeroEquivalentSize(width, level);
|
||||||
|
final int h = layersInHeight ? height : levelZeroEquivalentSize(height, level);
|
||||||
|
final int d = layersInDepth ? depth : levelZeroEquivalentSize(depth, level);
|
||||||
|
final int hLimit = layersInHeight ? maxArrayTextureLayers : dimLimit;
|
||||||
|
final int dLimit = layersInDepth ? maxArrayTextureLayers : dimLimit;
|
||||||
|
final boolean fits = w >= 1 && w <= dimLimit && h >= 1 && h <= hLimit && d >= 1 && d <= dLimit && formatSupported;
|
||||||
|
if (fits) {
|
||||||
|
accept(target, w, h, d, internalFormat);
|
||||||
|
} else {
|
||||||
|
reject(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reject(int target) {
|
||||||
|
accept(target, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void accept(int target, int width, int height, int depth, int internalFormat) {
|
||||||
|
this.target = target;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.depth = depth;
|
||||||
|
this.internalFormat = internalFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int levelParameter(int target, int level, int pname) {
|
||||||
|
if (this.target != target) return 0;
|
||||||
|
return switch (pname) {
|
||||||
|
case GL11.GL_TEXTURE_WIDTH -> TextureInfo.mipLevelSize(width, level);
|
||||||
|
case GL11.GL_TEXTURE_HEIGHT -> layersInHeight(target) ? height : TextureInfo.mipLevelSize(height, level);
|
||||||
|
case GL12.GL_TEXTURE_DEPTH -> layersInDepth(target) ? depth : TextureInfo.mipLevelSize(depth, level);
|
||||||
|
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> internalFormat;
|
||||||
|
default -> 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public int dimensionLimit(int target) {
|
||||||
|
return switch (target) {
|
||||||
|
case GL12.GL_PROXY_TEXTURE_3D -> max3DTextureSize;
|
||||||
|
case GL13.GL_PROXY_TEXTURE_CUBE_MAP, GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY -> maxCubeMapTextureSize;
|
||||||
|
default -> maxTextureSize;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int levelZeroEquivalentSize(int size, int level) {
|
||||||
|
if (size <= 0 || level < 0 || level >= Integer.SIZE - 1) return 0;
|
||||||
|
final long scaled = (long) size << level;
|
||||||
|
return scaled > Integer.MAX_VALUE ? 0 : (int) scaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean layersInHeight(int target) {
|
||||||
|
return target == GL30.GL_PROXY_TEXTURE_1D_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean layersInDepth(int target) {
|
||||||
|
return target == GL30.GL_PROXY_TEXTURE_2D_ARRAY || target == GL32.GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY || target == GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,12 @@ public class TextureInfo {
|
|||||||
private static final int UNSET_INT = Integer.MIN_VALUE;
|
private static final int UNSET_INT = Integer.MIN_VALUE;
|
||||||
private static final float UNSET_FLOAT = Float.NEGATIVE_INFINITY;
|
private static final float UNSET_FLOAT = Float.NEGATIVE_INFINITY;
|
||||||
|
|
||||||
|
public static int mipLevelSize(int base, int level) {
|
||||||
|
if (base <= 0 || level < 0) return 0;
|
||||||
|
if (level >= Integer.SIZE - 1) return 1;
|
||||||
|
return Math.max(1, base >> level);
|
||||||
|
}
|
||||||
|
|
||||||
@Getter protected final int id;
|
@Getter protected final int id;
|
||||||
@Getter @Setter protected int target = GL11.GL_TEXTURE_2D;
|
@Getter @Setter protected int target = GL11.GL_TEXTURE_2D;
|
||||||
protected int internalFormat = -1;
|
protected int internalFormat = -1;
|
||||||
|
|||||||
+33
@@ -277,6 +277,39 @@ class SpirvShaderTranslatorTest {
|
|||||||
assertFalse(es.contains("!= 0u"), "no bool use-site should still be comparing against 0u");
|
assertFalse(es.contains("!= 0u"), "no bool use-site should still be comparing against 0u");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void uniformBlock_staysABlockUnderGLES() {
|
||||||
|
final String src = """
|
||||||
|
#version 330 core
|
||||||
|
layout(std140) uniform Blk {
|
||||||
|
mat4 u_M;
|
||||||
|
vec4 u_C;
|
||||||
|
};
|
||||||
|
layout(std140) uniform Named {
|
||||||
|
vec4 u_N;
|
||||||
|
} named;
|
||||||
|
uniform float u_Loose;
|
||||||
|
struct L { vec4 c; };
|
||||||
|
uniform L u_Light;
|
||||||
|
in vec4 color;
|
||||||
|
out vec4 fragColor;
|
||||||
|
void main() {
|
||||||
|
fragColor = u_M * color * u_C * u_Loose + u_Light.c + named.u_N;
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
final String es = SpirvShaderTranslator.glslToGlslEs(src, GL20.GL_FRAGMENT_SHADER, "uniform_block.frag");
|
||||||
|
dumpOnFailure("uniform_block.frag", src, es);
|
||||||
|
|
||||||
|
assertNotNull(es);
|
||||||
|
assertTrue(Pattern.compile("layout\\s*\\(\\s*std140\\s*\\)\\s*uniform\\s+Blk\\s*\\{").matcher(es).find(), "a source uniform block must reach GLSL ES as a block, so glUniformBlockBinding finds it by name\n\n" + es);
|
||||||
|
assertFalse(Pattern.compile("uniform\\s+Blk\\s+_").matcher(es).find(), "the block must not be left as a plain struct uniform\n\n" + es);
|
||||||
|
assertFalse(Pattern.compile("_[0-9]+\\.u_M").matcher(es).find(), "an unnamed source block's members must be accessed bare\n\n" + es);
|
||||||
|
assertTrue(Pattern.compile("layout\\s*\\(\\s*std140\\s*\\)\\s*uniform\\s+Named\\s*\\{[^}]*\\}\\s*named\\s*;").matcher(es).find(), "an instance-named block keeps its instance name\n\n" + es);
|
||||||
|
assertTrue(es.contains("named.u_N"), "members of an instance-named block stay qualified\n\n" + es);
|
||||||
|
assertContainsDecl(es, "float", "u_Loose");
|
||||||
|
assertTrue(Pattern.compile("uniform\\s+L\\s+u_Light\\s*;").matcher(es).find(), "a struct uniform is not a block and must stay a plain uniform\n\n" + es);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void readonlyImage3D_staysAnImageUnderGLES() {
|
void readonlyImage3D_staysAnImageUnderGLES() {
|
||||||
final String src = """
|
final String src = """
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import java.time.Duration
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`java-library`
|
`java-library`
|
||||||
`maven-publish`
|
`maven-publish`
|
||||||
@@ -75,6 +77,80 @@ dependencies {
|
|||||||
compileOnly(libs.annotations)
|
compileOnly(libs.annotations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val glesTag = "gles"
|
||||||
|
|
||||||
|
val glsmRedirectAgent: Configuration by configurations.creating {
|
||||||
|
isCanBeConsumed = false
|
||||||
|
isCanBeResolved = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val glsmLwjgl2Stubs: Configuration by configurations.creating {
|
||||||
|
isCanBeConsumed = false
|
||||||
|
isCanBeResolved = true
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
glsmRedirectAgent(project(":glsm", "redirectAgent"))
|
||||||
|
glsmLwjgl2Stubs(project(":glsm", "lwjgl2Stubs"))
|
||||||
|
|
||||||
|
testImplementation(platform(libs.junit.bom))
|
||||||
|
testImplementation(libs.junit.jupiter)
|
||||||
|
testImplementation(testFixtures(project(":glsm")))
|
||||||
|
testImplementation(project(":glsm")) {
|
||||||
|
capabilities { requireCapability("${project.group}:glsm-stubs") }
|
||||||
|
}
|
||||||
|
listOf(libs.lwjgl3.sdl, libs.lwjgl3.shaderc, libs.lwjgl3.spvc).forEach {
|
||||||
|
testImplementation(it)
|
||||||
|
testRuntimeOnly(it) { artifact { classifier = lwjglNatives } }
|
||||||
|
}
|
||||||
|
testRuntimeOnly(libs.junit.platform.launcher)
|
||||||
|
testRuntimeOnly(libs.log4j.core)
|
||||||
|
testRuntimeOnly(libs.fastutil)
|
||||||
|
testRuntimeOnly(libs.joml)
|
||||||
|
testRuntimeOnly(libs.commons.lang3)
|
||||||
|
testRuntimeOnly(libs.guava)
|
||||||
|
testRuntimeOnly(libs.celeritas.common) { isTransitive = false }
|
||||||
|
testRuntimeOnly(libs.asm.tree.test)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform {
|
||||||
|
excludeTags(glesTag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val unpackLwjgl3Natives = registerLwjgl3NativesUnpack()
|
||||||
|
|
||||||
|
val glesTest by tasks.registering(Test::class) {
|
||||||
|
description = "Boots GLSM on the LWJGL3 GL backend in a real OpenGL ES 3.2 context."
|
||||||
|
group = "verification"
|
||||||
|
testClassesDirs = sourceSets["test"].output.classesDirs
|
||||||
|
classpath = glsmLwjgl2Stubs.plus(
|
||||||
|
sourceSets["test"].runtimeClasspath.filter { file ->
|
||||||
|
!file.name.startsWith("lwjgl-2.") && !file.name.startsWith("lwjgl_util-") && !file.name.startsWith("lwjgl-platform-")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
useJUnitPlatform {
|
||||||
|
includeTags(glesTag)
|
||||||
|
}
|
||||||
|
systemProperty("glsm.stubs.capabilities", "lwjgl3")
|
||||||
|
javaAgent(glsmRedirectAgent)
|
||||||
|
loadLwjgl3NativesFrom(unpackLwjgl3Natives)
|
||||||
|
outputs.cacheIf { false }
|
||||||
|
timeout = Duration.ofMinutes(5)
|
||||||
|
systemProperty("junit.jupiter.execution.timeout.default", "60s")
|
||||||
|
systemProperty("junit.jupiter.execution.timeout.threaddump.enabled", "true")
|
||||||
|
testLogging {
|
||||||
|
events("passed", "skipped", "failed")
|
||||||
|
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||||
|
}
|
||||||
|
mustRunAfter(tasks.test)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!providers.environmentVariable("CI").isPresent) {
|
||||||
|
tasks.check { dependsOn(glesTest) }
|
||||||
|
}
|
||||||
|
|
||||||
tasks.named<JavaCompile>("compileJava") {
|
tasks.named<JavaCompile>("compileJava") {
|
||||||
val classesDir = destinationDirectory
|
val classesDir = destinationDirectory
|
||||||
doLast { injectLwjgl3Aware(classesDir.get().asFile) }
|
doLast { injectLwjgl3Aware(classesDir.get().asFile) }
|
||||||
|
|||||||
+59
-9
@@ -10,6 +10,9 @@ import com.gtnewhorizons.angelica.glsm.GLScreenCapture;
|
|||||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||||
import com.gtnewhorizons.angelica.glsm.backend.ScreenCapture;
|
import com.gtnewhorizons.angelica.glsm.backend.ScreenCapture;
|
||||||
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.texture.ProxyTexture;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import me.eigenraven.lwjgl3ify.api.Lwjgl3Aware;
|
import me.eigenraven.lwjgl3ify.api.Lwjgl3Aware;
|
||||||
import me.eigenraven.lwjgl3ify.client.MainThreadExec;
|
import me.eigenraven.lwjgl3ify.client.MainThreadExec;
|
||||||
import org.lwjgl.PointerBuffer;
|
import org.lwjgl.PointerBuffer;
|
||||||
@@ -48,6 +51,7 @@ import org.lwjgl.sdl.SDLError;
|
|||||||
import org.lwjgl.sdl.SDLEvents;
|
import org.lwjgl.sdl.SDLEvents;
|
||||||
import org.lwjgl.sdl.SDLTimer;
|
import org.lwjgl.sdl.SDLTimer;
|
||||||
import org.lwjgl.sdl.SDLVideo;
|
import org.lwjgl.sdl.SDLVideo;
|
||||||
|
import org.lwjgl.system.FunctionProvider;
|
||||||
import org.lwjgl.system.JNI;
|
import org.lwjgl.system.JNI;
|
||||||
import org.lwjgl.system.MemoryStack;
|
import org.lwjgl.system.MemoryStack;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
@@ -97,14 +101,45 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
|||||||
|| normalizedVendor.startsWith("advanced micro devices");
|
|| normalizedVendor.startsWith("advanced micro devices");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
final FunctionProvider functions = GL.getFunctionProvider();
|
||||||
if (RenderSystem.isGLES()) {
|
if (RenderSystem.isGLES()) {
|
||||||
pfnClearDepthf = GL.getFunctionProvider().getFunctionAddress("glClearDepthf");
|
pfnClearDepthf = functions.getFunctionAddress("glClearDepthf");
|
||||||
pfnDepthRangef = GL.getFunctionProvider().getFunctionAddress("glDepthRangef");
|
pfnDepthRangef = functions.getFunctionAddress("glDepthRangef");
|
||||||
|
proxyTexture = new ProxyTexture(GL11C.glGetInteger(GL11C.GL_MAX_TEXTURE_SIZE), GL11C.glGetInteger(GL12C.GL_MAX_3D_TEXTURE_SIZE),
|
||||||
|
GL11C.glGetInteger(GL13C.GL_MAX_CUBE_MAP_TEXTURE_SIZE), GL11C.glGetInteger(GL30C.GL_MAX_ARRAY_TEXTURE_LAYERS));
|
||||||
}
|
}
|
||||||
|
pfnGenSamplers = requireFunction(functions, "glGenSamplers");
|
||||||
|
pfnDeleteSamplers = requireFunction(functions, "glDeleteSamplers");
|
||||||
|
pfnBindSampler = requireFunction(functions, "glBindSampler");
|
||||||
|
pfnSamplerParameteri = requireFunction(functions, "glSamplerParameteri");
|
||||||
|
pfnSamplerParameterf = requireFunction(functions, "glSamplerParameterf");
|
||||||
|
pfnIsSampler = requireFunction(functions, "glIsSampler");
|
||||||
|
pfnVertexAttribDivisor = requireFunction(functions, "glVertexAttribDivisor");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long requireFunction(FunctionProvider functions, String name) {
|
||||||
|
final long address = functions.getFunctionAddress(name);
|
||||||
|
if (address == 0L) throw new IllegalStateException(name + " is not exported by the GL driver");
|
||||||
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long pfnClearDepthf;
|
private long pfnClearDepthf;
|
||||||
private long pfnDepthRangef;
|
private long pfnDepthRangef;
|
||||||
|
private @Nullable ProxyTexture proxyTexture;
|
||||||
|
// GL33C slots stay unloaded on an ES 3.2 context: LWJGL builds desktop caps only up to the reported 3.2.
|
||||||
|
private long pfnGenSamplers;
|
||||||
|
private long pfnDeleteSamplers;
|
||||||
|
private long pfnBindSampler;
|
||||||
|
private long pfnSamplerParameteri;
|
||||||
|
private long pfnSamplerParameterf;
|
||||||
|
private long pfnIsSampler;
|
||||||
|
private long pfnVertexAttribDivisor;
|
||||||
|
|
||||||
|
private boolean recordProxy(int target, int level, int internalFormat, int width, int height, int depth) {
|
||||||
|
if (proxyTexture == null || !TextureInfoCache.isProxyTarget(target)) return false;
|
||||||
|
proxyTexture.record(target, level, internalFormat, width, height, depth, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
@@ -565,26 +600,31 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, ByteBuffer pixels) {
|
||||||
|
if (recordProxy(target, level, internalFormat, width, height, 1)) return;
|
||||||
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels);
|
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, DoubleBuffer pixels) {
|
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, DoubleBuffer pixels) {
|
||||||
|
if (recordProxy(target, level, internalFormat, width, height, 1)) return;
|
||||||
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels);
|
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, FloatBuffer pixels) {
|
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, FloatBuffer pixels) {
|
||||||
|
if (recordProxy(target, level, internalFormat, width, height, 1)) return;
|
||||||
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels);
|
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, IntBuffer pixels) {
|
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, IntBuffer pixels) {
|
||||||
|
if (recordProxy(target, level, internalFormat, width, height, 1)) return;
|
||||||
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels);
|
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, long pixelBufferOffset) {
|
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, long pixelBufferOffset) {
|
||||||
|
if (recordProxy(target, level, internalFormat, width, height, 1)) return;
|
||||||
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixelBufferOffset);
|
GL11C.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixelBufferOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,16 +645,19 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void texImage1D(int target, int level, int internalFormat, int width, int border, int format, int type, ByteBuffer pixels) {
|
public void texImage1D(int target, int level, int internalFormat, int width, int border, int format, int type, ByteBuffer pixels) {
|
||||||
|
if (recordProxy(target, level, internalFormat, width, 1, 1)) return;
|
||||||
GL11C.glTexImage1D(target, level, internalFormat, width, border, format, type, pixels);
|
GL11C.glTexImage1D(target, level, internalFormat, width, border, format, type, pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void texImage3D(int target, int level, int internalFormat, int width, int height, int depth, int border, int format, int type, ByteBuffer pixels) {
|
public void texImage3D(int target, int level, int internalFormat, int width, int height, int depth, int border, int format, int type, ByteBuffer pixels) {
|
||||||
|
if (recordProxy(target, level, internalFormat, width, height, depth)) return;
|
||||||
GL12C.glTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, pixels);
|
GL12C.glTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void texImage3D(int target, int level, int internalFormat, int width, int height, int depth, int border, int format, int type, IntBuffer pixels) {
|
public void texImage3D(int target, int level, int internalFormat, int width, int height, int depth, int border, int format, int type, IntBuffer pixels) {
|
||||||
|
if (recordProxy(target, level, internalFormat, width, height, depth)) return;
|
||||||
GL12C.glTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, pixels);
|
GL12C.glTexImage3D(target, level, internalFormat, width, height, depth, border, format, type, pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,6 +729,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getTexLevelParameteri(int target, int level, int pname) {
|
public int getTexLevelParameteri(int target, int level, int pname) {
|
||||||
|
if (proxyTexture != null && TextureInfoCache.isProxyTarget(target)) return proxyTexture.levelParameter(target, level, pname);
|
||||||
return GL11C.glGetTexLevelParameteri(target, level, pname);
|
return GL11C.glGetTexLevelParameteri(target, level, pname);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -706,27 +750,33 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int genSamplers() {
|
public int genSamplers() {
|
||||||
return GL33C.glGenSamplers();
|
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||||
|
final IntBuffer ids = stack.mallocInt(1);
|
||||||
|
JNI.callPV(1, MemoryUtil.memAddress(ids), pfnGenSamplers);
|
||||||
|
return ids.get(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteSamplers(int sampler) {
|
public void deleteSamplers(int sampler) {
|
||||||
GL33C.glDeleteSamplers(sampler);
|
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||||
|
JNI.callPV(1, MemoryUtil.memAddress(stack.ints(sampler)), pfnDeleteSamplers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindSampler(int unit, int sampler) {
|
public void bindSampler(int unit, int sampler) {
|
||||||
GL33C.glBindSampler(unit, sampler);
|
JNI.callV(unit, sampler, pfnBindSampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void samplerParameteri(int sampler, int pname, int param) {
|
public void samplerParameteri(int sampler, int pname, int param) {
|
||||||
GL33C.glSamplerParameteri(sampler, pname, param);
|
JNI.callV(sampler, pname, param, pfnSamplerParameteri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void samplerParameterf(int sampler, int pname, float param) {
|
public void samplerParameterf(int sampler, int pname, float param) {
|
||||||
GL33C.glSamplerParameterf(sampler, pname, param);
|
JNI.callV(sampler, pname, param, pfnSamplerParameterf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1371,7 +1421,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
|||||||
|
|
||||||
@Override public boolean isFramebuffer(int framebuffer) { return GL30C.glIsFramebuffer(framebuffer); }
|
@Override public boolean isFramebuffer(int framebuffer) { return GL30C.glIsFramebuffer(framebuffer); }
|
||||||
@Override public boolean isRenderbuffer(int renderbuffer) { return GL30C.glIsRenderbuffer(renderbuffer); }
|
@Override public boolean isRenderbuffer(int renderbuffer) { return GL30C.glIsRenderbuffer(renderbuffer); }
|
||||||
@Override public boolean isSampler(int sampler) { return GL33C.glIsSampler(sampler); }
|
@Override public boolean isSampler(int sampler) { return JNI.callZ(sampler, pfnIsSampler); }
|
||||||
@Override public boolean isQuery(int query) { return GL15C.glIsQuery(query); }
|
@Override public boolean isQuery(int query) { return GL15C.glIsQuery(query); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1441,7 +1491,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void vertexAttribDivisor(int index, int divisor) {
|
public void vertexAttribDivisor(int index, int divisor) {
|
||||||
GL33C.glVertexAttribDivisor(index, divisor);
|
JNI.callV(index, divisor, pfnVertexAttribDivisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,201 @@
|
|||||||
|
package com.gtnewhorizons.angelica.lwjgl3;
|
||||||
|
|
||||||
|
import com.gtnewhorizons.angelica.glsm.GLSMCurrentAttribs;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.GLSMBlend;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.GLSMDraws;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.GLSMEnable;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.GLSMFramebuffer;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.GLSMRasterizer;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.GLSMTextures;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.GLSMVertexInput;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.GLStateManager;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.RenderSystem;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.backend.BackendManager;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.condition.DisabledOnOs;
|
||||||
|
import org.junit.jupiter.api.condition.OS;
|
||||||
|
import org.lwjgl.opengl.GL;
|
||||||
|
import org.lwjgl.opengl.GL11C;
|
||||||
|
import org.lwjgl.opengl.GL12C;
|
||||||
|
import org.lwjgl.opengl.GL20C;
|
||||||
|
import org.lwjgl.opengl.GL30C;
|
||||||
|
import org.lwjgl.opengl.GL33C;
|
||||||
|
import org.lwjgl.sdl.SDLError;
|
||||||
|
import org.lwjgl.sdl.SDLInit;
|
||||||
|
import org.lwjgl.sdl.SDLVideo;
|
||||||
|
import org.lwjgl.system.Configuration;
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@Tag("gles")
|
||||||
|
@DisabledOnOs(OS.MAC)
|
||||||
|
class GlesSmokeTest {
|
||||||
|
|
||||||
|
private static final int SIZE = 64;
|
||||||
|
private static int fbo;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void boot() {
|
||||||
|
Configuration.OPENGL_EXPLICIT_INIT.set(true);
|
||||||
|
assertTrue(SDLInit.SDL_Init(SDLInit.SDL_INIT_VIDEO), () -> "SDL_Init failed: " + SDLError.SDL_GetError());
|
||||||
|
SDLVideo.SDL_GL_SetAttribute(SDLVideo.SDL_GL_CONTEXT_PROFILE_MASK, SDLVideo.SDL_GL_CONTEXT_PROFILE_ES);
|
||||||
|
SDLVideo.SDL_GL_SetAttribute(SDLVideo.SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||||
|
SDLVideo.SDL_GL_SetAttribute(SDLVideo.SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||||
|
SDLVideo.SDL_GL_SetAttribute(SDLVideo.SDL_GL_DEPTH_SIZE, 24);
|
||||||
|
SDLVideo.SDL_GL_SetAttribute(SDLVideo.SDL_GL_STENCIL_SIZE, 8);
|
||||||
|
final long window = SDLVideo.SDL_CreateWindow("glesSmoke", SIZE, SIZE, SDLVideo.SDL_WINDOW_OPENGL | SDLVideo.SDL_WINDOW_HIDDEN);
|
||||||
|
assertNotEquals(0L, window, () -> "SDL_CreateWindow failed: " + SDLError.SDL_GetError());
|
||||||
|
final long context = SDLVideo.SDL_GL_CreateContext(window);
|
||||||
|
assertNotEquals(0L, context, () -> "SDL_GL_CreateContext failed: " + SDLError.SDL_GetError());
|
||||||
|
assertTrue(SDLVideo.SDL_GL_MakeCurrent(window, context), () -> "SDL_GL_MakeCurrent failed: " + SDLError.SDL_GetError());
|
||||||
|
|
||||||
|
GL.create(SDLVideo::SDL_GL_GetProcAddress);
|
||||||
|
GL.createCapabilities();
|
||||||
|
// LWJGL's GL_CONTEXT_PROFILE_MASK probe in createCapabilities leaves INVALID_ENUM on an ES context.
|
||||||
|
while (GL11C.glGetError() != GL11C.GL_NO_ERROR) {}
|
||||||
|
|
||||||
|
final String version = GL11C.glGetString(GL11C.GL_VERSION);
|
||||||
|
assertTrue(version != null && version.startsWith("OpenGL ES 3."), () -> "not an ES 3.x context: " + version);
|
||||||
|
assertTrue(BackendManager.RENDER_BACKEND instanceof Lwjgl3GLRenderBackend, () -> "selected backend is " + BackendManager.RENDER_BACKEND.getName());
|
||||||
|
|
||||||
|
GLStateManager.initialize(GLSMInitConfig.builder().displaySize(SIZE, SIZE).build());
|
||||||
|
GLStateManager.setRunningSplash(false);
|
||||||
|
GLStateManager.markSplashComplete("glesSmoke");
|
||||||
|
|
||||||
|
assertTrue(RenderSystem.isGLES());
|
||||||
|
assertTrue(RenderSystem.supportsSamplerObjects());
|
||||||
|
fbo = createTarget();
|
||||||
|
assertEquals(GL11C.GL_NO_ERROR, GL11C.glGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void samplerLifecycle() {
|
||||||
|
final int sampler = RenderSystem.genSampler();
|
||||||
|
assertNotEquals(0, sampler);
|
||||||
|
RenderSystem.samplerParameteri(sampler, GL11C.GL_TEXTURE_MIN_FILTER, GL11C.GL_LINEAR);
|
||||||
|
RenderSystem.samplerParameterf(sampler, GL12C.GL_TEXTURE_MAX_LOD, 4.0f);
|
||||||
|
RenderSystem.bindSamplerToUnit(0, sampler);
|
||||||
|
assertEquals(sampler, GL11C.glGetInteger(GL33C.GL_SAMPLER_BINDING));
|
||||||
|
assertTrue(BackendManager.RENDER_BACKEND.isSampler(sampler));
|
||||||
|
|
||||||
|
RenderSystem.destroySampler(sampler);
|
||||||
|
assertFalse(BackendManager.RENDER_BACKEND.isSampler(sampler));
|
||||||
|
assertEquals(GL11C.GL_NO_ERROR, GL11C.glGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void vertexAttribDivisor() {
|
||||||
|
final int vao = GLSMVertexInput.glGenVertexArrays();
|
||||||
|
GLSMVertexInput.glBindVertexArray(vao);
|
||||||
|
GLSMVertexInput.glVertexAttribDivisor(5, 1);
|
||||||
|
assertEquals(1, GL20C.glGetVertexAttribi(5, GL33C.GL_VERTEX_ATTRIB_ARRAY_DIVISOR));
|
||||||
|
GLSMVertexInput.glBindVertexArray(0);
|
||||||
|
assertEquals(GL11C.GL_NO_ERROR, GL11C.glGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ffpQuadRenders() {
|
||||||
|
bindClearedTarget();
|
||||||
|
GLSMDraws.glBegin(GL11C.GL_QUADS);
|
||||||
|
GLSMCurrentAttribs.glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
|
||||||
|
GLSMDraws.glVertex3f(-1.0f, -1.0f, 0.0f);
|
||||||
|
GLSMDraws.glVertex3f(1.0f, -1.0f, 0.0f);
|
||||||
|
GLSMDraws.glVertex3f(1.0f, 1.0f, 0.0f);
|
||||||
|
GLSMDraws.glVertex3f(-1.0f, 1.0f, 0.0f);
|
||||||
|
GLSMDraws.glEnd();
|
||||||
|
|
||||||
|
assertEquals(0xFF00FF00, readCenterPixel());
|
||||||
|
assertEquals(GL11C.GL_NO_ERROR, GL11C.glGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void ffpTexturedQuadRenders() {
|
||||||
|
final int texel = 0xFF2060A0;
|
||||||
|
final int texture = GLSMTextures.glGenTextures();
|
||||||
|
GLSMTextures.glBindTexture(GL11C.GL_TEXTURE_2D, texture);
|
||||||
|
GLSMTextures.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MIN_FILTER, GL11C.GL_NEAREST);
|
||||||
|
GLSMTextures.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MAG_FILTER, GL11C.GL_NEAREST);
|
||||||
|
final IntBuffer pixels = MemoryUtil.memAllocInt(16);
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < 16; i++) pixels.put(i, texel);
|
||||||
|
GLSMTextures.glTexImage2D(GL11C.GL_TEXTURE_2D, 0, GL11C.GL_RGBA, 4, 4, 0, GL12C.GL_BGRA, GL12C.GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
|
||||||
|
} finally {
|
||||||
|
MemoryUtil.memFree(pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
bindClearedTarget();
|
||||||
|
GLSMTextures.glBindTexture(GL11C.GL_TEXTURE_2D, texture);
|
||||||
|
GLSMEnable.glEnable(GL11C.GL_TEXTURE_2D);
|
||||||
|
GLSMDraws.glBegin(GL11C.GL_QUADS);
|
||||||
|
GLSMCurrentAttribs.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
GLSMCurrentAttribs.glTexCoord2f(0.0f, 0.0f);
|
||||||
|
GLSMDraws.glVertex3f(-1.0f, -1.0f, 0.0f);
|
||||||
|
GLSMCurrentAttribs.glTexCoord2f(1.0f, 0.0f);
|
||||||
|
GLSMDraws.glVertex3f(1.0f, -1.0f, 0.0f);
|
||||||
|
GLSMCurrentAttribs.glTexCoord2f(1.0f, 1.0f);
|
||||||
|
GLSMDraws.glVertex3f(1.0f, 1.0f, 0.0f);
|
||||||
|
GLSMCurrentAttribs.glTexCoord2f(0.0f, 1.0f);
|
||||||
|
GLSMDraws.glVertex3f(-1.0f, 1.0f, 0.0f);
|
||||||
|
GLSMDraws.glEnd();
|
||||||
|
GLSMEnable.glDisable(GL11C.GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
assertEquals(texel, readCenterPixel());
|
||||||
|
assertEquals(GL11C.GL_NO_ERROR, GL11C.glGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void proxyTextureProbeFindsTheMaxTextureSize() {
|
||||||
|
final int maxSize = Integer.highestOneBit(GL11C.glGetInteger(GL11C.GL_MAX_TEXTURE_SIZE));
|
||||||
|
int found = -1;
|
||||||
|
for (int size = maxSize * 2; size > 0 && found < 0; size >>= 1) {
|
||||||
|
GLSMTextures.glTexImage2D(GL11C.GL_PROXY_TEXTURE_2D, 0, GL11C.GL_RGBA, size, size, 0, GL11C.GL_RGBA, GL11C.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||||
|
if (GLSMTextures.glGetTexLevelParameteri(GL11C.GL_PROXY_TEXTURE_2D, 0, GL11C.GL_TEXTURE_WIDTH) != 0) found = size;
|
||||||
|
}
|
||||||
|
assertEquals(maxSize, found);
|
||||||
|
assertEquals(GL11C.GL_NO_ERROR, GL11C.glGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void bindClearedTarget() {
|
||||||
|
GLSMFramebuffer.glBindFramebuffer(GL30C.GL_FRAMEBUFFER, fbo);
|
||||||
|
GLSMRasterizer.glViewport(0, 0, SIZE, SIZE);
|
||||||
|
GLSMBlend.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
GLSMFramebuffer.glClear(GL11C.GL_COLOR_BUFFER_BIT | GL11C.GL_DEPTH_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readCenterPixel() {
|
||||||
|
final ByteBuffer pixel = MemoryUtil.memAlloc(4);
|
||||||
|
try {
|
||||||
|
GLSMFramebuffer.glReadPixels(SIZE / 2, SIZE / 2, 1, 1, GL11C.GL_RGBA, GL11C.GL_UNSIGNED_BYTE, pixel);
|
||||||
|
return (pixel.get(3) & 0xFF) << 24 | (pixel.get(0) & 0xFF) << 16 | (pixel.get(1) & 0xFF) << 8 | pixel.get(2) & 0xFF;
|
||||||
|
} finally {
|
||||||
|
MemoryUtil.memFree(pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int createTarget() {
|
||||||
|
final int color = GLSMTextures.glGenTextures();
|
||||||
|
GLSMTextures.glBindTexture(GL11C.GL_TEXTURE_2D, color);
|
||||||
|
GLSMTextures.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MIN_FILTER, GL11C.GL_NEAREST);
|
||||||
|
GLSMTextures.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MAG_FILTER, GL11C.GL_NEAREST);
|
||||||
|
GLSMTextures.glTexImage2D(GL11C.GL_TEXTURE_2D, 0, GL11C.GL_RGBA8, SIZE, SIZE, 0, GL11C.GL_RGBA, GL11C.GL_UNSIGNED_BYTE, (ByteBuffer) null);
|
||||||
|
final int depth = GLSMFramebuffer.glGenRenderbuffers();
|
||||||
|
GLSMFramebuffer.glBindRenderbuffer(GL30C.GL_RENDERBUFFER, depth);
|
||||||
|
GLSMFramebuffer.glRenderbufferStorage(GL30C.GL_RENDERBUFFER, GL30C.GL_DEPTH24_STENCIL8, SIZE, SIZE);
|
||||||
|
final int target = GLSMFramebuffer.glGenFramebuffers();
|
||||||
|
GLSMFramebuffer.glBindFramebuffer(GL30C.GL_FRAMEBUFFER, target);
|
||||||
|
GLSMFramebuffer.glFramebufferTexture2D(GL30C.GL_FRAMEBUFFER, GL30C.GL_COLOR_ATTACHMENT0, GL11C.GL_TEXTURE_2D, color, 0);
|
||||||
|
GLSMFramebuffer.glFramebufferRenderbuffer(GL30C.GL_FRAMEBUFFER, GL30C.GL_DEPTH_STENCIL_ATTACHMENT, GL30C.GL_RENDERBUFFER, depth);
|
||||||
|
GLSMFramebuffer.glBindFramebuffer(GL30C.GL_FRAMEBUFFER, 0);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -106,7 +106,13 @@ val glsmSdlAgent = sourceSets.create("glsmSdlAgent") {
|
|||||||
java.srcDir("src/glsmSdlAgent/java")
|
java.srcDir("src/glsmSdlAgent/java")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val glsmLwjgl2Stubs: Configuration by configurations.creating {
|
||||||
|
isCanBeConsumed = false
|
||||||
|
isCanBeResolved = true
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
glsmLwjgl2Stubs(project(":glsm", "lwjgl2Stubs"))
|
||||||
"glsmSdlAgentCompileOnly"(project(":glsm"))
|
"glsmSdlAgentCompileOnly"(project(":glsm"))
|
||||||
"glsmSdlAgentCompileOnly"(testFixtures(project(":glsm")))
|
"glsmSdlAgentCompileOnly"(testFixtures(project(":glsm")))
|
||||||
"glsmSdlAgentCompileOnly"(libs.asm.tree.test)
|
"glsmSdlAgentCompileOnly"(libs.asm.tree.test)
|
||||||
@@ -148,7 +154,7 @@ val glsmSdlTest by tasks.registering(Test::class) {
|
|||||||
description = "Runs the headless GLSM-on-SDL FFP rig in its own JVM, with the SDL GPU gate engaged."
|
description = "Runs the headless GLSM-on-SDL FFP rig in its own JVM, with the SDL GPU gate engaged."
|
||||||
group = "verification"
|
group = "verification"
|
||||||
testClassesDirs = sourceSets["test"].output.classesDirs
|
testClassesDirs = sourceSets["test"].output.classesDirs
|
||||||
classpath = glsmSdlStubs.output.classesDirs.plus(
|
classpath = glsmSdlStubs.output.classesDirs.plus(glsmLwjgl2Stubs).plus(
|
||||||
sourceSets["test"].runtimeClasspath.filter { file ->
|
sourceSets["test"].runtimeClasspath.filter { file ->
|
||||||
!file.name.startsWith("lwjgl-2.") && !file.name.startsWith("lwjgl_util-") && !file.name.startsWith("lwjgl-platform-")
|
!file.name.startsWith("lwjgl-2.") && !file.name.startsWith("lwjgl_util-") && !file.name.startsWith("lwjgl-platform-")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
package org.lwjgl.opengl;
|
|
||||||
|
|
||||||
public final class GLContext {
|
|
||||||
|
|
||||||
private static final ContextCapabilities CAPABILITIES = new ContextCapabilities();
|
|
||||||
|
|
||||||
private GLContext() {}
|
|
||||||
|
|
||||||
public static ContextCapabilities getCapabilities() {
|
|
||||||
return CAPABILITIES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,6 +20,7 @@ import com.gtnewhorizons.angelica.glsm.backend.VSyncMode;
|
|||||||
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
import com.gtnewhorizons.angelica.glsm.backend.RenderBackend;
|
||||||
import com.gtnewhorizons.angelica.glsm.backend.ScreenCapture;
|
import com.gtnewhorizons.angelica.glsm.backend.ScreenCapture;
|
||||||
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
import com.gtnewhorizons.angelica.glsm.hooks.GLSMConfig;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||||
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
import com.gtnewhorizons.angelica.glsm.texture.TextureInfoCache;
|
||||||
import com.gtnewhorizons.angelica.glsm.texture.TextureStaging;
|
import com.gtnewhorizons.angelica.glsm.texture.TextureStaging;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.compute.ComputeBinder;
|
import com.gtnewhorizons.angelica.sdlgpu.compute.ComputeBinder;
|
||||||
@@ -1507,52 +1508,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
|||||||
return TextureInfoCache.isProxyTarget(target);
|
return TextureInfoCache.isProxyTarget(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean proxyLayersInHeight(int target) {
|
|
||||||
return target == GL30.GL_PROXY_TEXTURE_1D_ARRAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean proxyLayersInDepth(int target) {
|
|
||||||
return isProxyArrayTarget(target) && !proxyLayersInHeight(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isProxyArrayTarget(int target) {
|
|
||||||
return target == GL30.GL_PROXY_TEXTURE_1D_ARRAY || target == GL30.GL_PROXY_TEXTURE_2D_ARRAY || target == GL32.GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY || target == GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int proxyLevelSize(int base, int level) {
|
|
||||||
return PixelOps.mipLevelSize(base, level);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int proxyDimensionLimit(int target) {
|
|
||||||
return switch (target) {
|
|
||||||
case GL12.GL_PROXY_TEXTURE_3D -> MAX_3D_TEXTURE_SIZE;
|
|
||||||
case GL13.GL_PROXY_TEXTURE_CUBE_MAP, GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY -> MAX_CUBE_MAP_TEXTURE_SIZE;
|
|
||||||
default -> MAX_TEXTURE_SIZE;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static int levelZeroEquivalentSize(int size, int level) {
|
|
||||||
if (size <= 0 || level < 0 || level >= Integer.SIZE - 1) return 0;
|
|
||||||
final long scaled = (long) size << level;
|
|
||||||
return scaled > Integer.MAX_VALUE ? 0 : (int) scaled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void recordProxyTexImage(ContextState st, int target, int level, int internalFormat, int width, int height, int depth) {
|
private void recordProxyTexImage(ContextState st, int target, int level, int internalFormat, int width, int height, int depth) {
|
||||||
final boolean layersInHeight = proxyLayersInHeight(target);
|
st.proxyTexture.record(target, level, internalFormat, width, height, depth, resourceManager.isMappableTextureFormat(internalFormat));
|
||||||
final boolean layersInDepth = proxyLayersInDepth(target);
|
|
||||||
final int dimLimit = proxyDimensionLimit(target);
|
|
||||||
final int w = levelZeroEquivalentSize(width, level);
|
|
||||||
final int h = layersInHeight ? height : levelZeroEquivalentSize(height, level);
|
|
||||||
final int d = layersInDepth ? depth : levelZeroEquivalentSize(depth, level);
|
|
||||||
final int hLimit = layersInHeight ? MAX_ARRAY_TEXTURE_LAYERS : dimLimit;
|
|
||||||
final int dLimit = layersInDepth ? MAX_ARRAY_TEXTURE_LAYERS : dimLimit;
|
|
||||||
final boolean fits = w >= 1 && w <= dimLimit && h >= 1 && h <= hLimit && d >= 1 && d <= dLimit
|
|
||||||
&& resourceManager.isMappableTextureFormat(internalFormat);
|
|
||||||
if (fits) {
|
|
||||||
st.proxyTexture.accept(target, w, h, d, internalFormat);
|
|
||||||
} else {
|
|
||||||
st.proxyTexture.reject(target);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int boundTextureFor(ContextState st, int target) {
|
private static int boundTextureFor(ContextState st, int target) {
|
||||||
@@ -1816,23 +1773,13 @@ public class SDLGPURenderBackend extends RenderBackend {
|
|||||||
}
|
}
|
||||||
@Override public int getTexLevelParameteri(int target, int level, int pname) {
|
@Override public int getTexLevelParameteri(int target, int level, int pname) {
|
||||||
final ContextState cs = s();
|
final ContextState cs = s();
|
||||||
if (isProxyTarget(target)) {
|
if (isProxyTarget(target)) return cs.proxyTexture.levelParameter(target, level, pname);
|
||||||
final ContextState.ProxyTextureState proxy = cs.proxyTexture;
|
|
||||||
if (proxy.target != target) return 0;
|
|
||||||
return switch (pname) {
|
|
||||||
case GL11.GL_TEXTURE_WIDTH -> proxyLevelSize(proxy.width, level);
|
|
||||||
case GL11.GL_TEXTURE_HEIGHT -> proxyLayersInHeight(target) ? proxy.height : proxyLevelSize(proxy.height, level);
|
|
||||||
case GL12.GL_TEXTURE_DEPTH -> proxyLayersInDepth(target) ? proxy.depth : proxyLevelSize(proxy.depth, level);
|
|
||||||
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> proxy.internalFormat;
|
|
||||||
default -> 0;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
final int glId = cs.boundTextures[cs.activeTextureUnit];
|
final int glId = cs.boundTextures[cs.activeTextureUnit];
|
||||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(glId);
|
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(glId);
|
||||||
if (meta == null) return 0;
|
if (meta == null) return 0;
|
||||||
return switch (pname) {
|
return switch (pname) {
|
||||||
case GL11.GL_TEXTURE_WIDTH -> PixelOps.mipLevelSize(meta.width(), level);
|
case GL11.GL_TEXTURE_WIDTH -> TextureInfo.mipLevelSize(meta.width(), level);
|
||||||
case GL11.GL_TEXTURE_HEIGHT -> PixelOps.mipLevelSize(meta.height(), level);
|
case GL11.GL_TEXTURE_HEIGHT -> TextureInfo.mipLevelSize(meta.height(), level);
|
||||||
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> {
|
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> {
|
||||||
final int stored = meta.glFormat();
|
final int stored = meta.glFormat();
|
||||||
yield TextureInfoCache.isGenericCompressedInternalFormat(stored) ? GL11.GL_RGBA8 : stored;
|
yield TextureInfoCache.isGenericCompressedInternalFormat(stored) ? GL11.GL_RGBA8 : stored;
|
||||||
@@ -1987,8 +1934,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
|||||||
if (meta != null) {
|
if (meta != null) {
|
||||||
fbo.colorFormats[colorIdx] = meta.sdlFormat();
|
fbo.colorFormats[colorIdx] = meta.sdlFormat();
|
||||||
if (colorIdx == 0) {
|
if (colorIdx == 0) {
|
||||||
fbo.width = PixelOps.mipLevelSize(meta.width(), level);
|
fbo.width = TextureInfo.mipLevelSize(meta.width(), level);
|
||||||
fbo.height = PixelOps.mipLevelSize(meta.height(), level);
|
fbo.height = TextureInfo.mipLevelSize(meta.height(), level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fbo.colorAttachmentCount = Math.max(fbo.colorAttachmentCount, colorIdx + 1);
|
fbo.colorAttachmentCount = Math.max(fbo.colorAttachmentCount, colorIdx + 1);
|
||||||
@@ -2216,8 +2163,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
|||||||
out.level = fbo.colorLevels[fbo.readBufferIndex];
|
out.level = fbo.colorLevels[fbo.readBufferIndex];
|
||||||
out.sdlFormat = meta != null ? meta.sdlFormat() : SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
out.sdlFormat = meta != null ? meta.sdlFormat() : SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
||||||
out.glBaseFormat = meta != null ? PixelPack.glBaseFormat(meta.glFormat()) : GL11.GL_RGBA;
|
out.glBaseFormat = meta != null ? PixelPack.glBaseFormat(meta.glFormat()) : GL11.GL_RGBA;
|
||||||
out.width = meta != null ? PixelOps.mipLevelSize(meta.width(), out.level) : fbo.width;
|
out.width = meta != null ? TextureInfo.mipLevelSize(meta.width(), out.level) : fbo.width;
|
||||||
out.height = meta != null ? PixelOps.mipLevelSize(meta.height(), out.level) : fbo.height;
|
out.height = meta != null ? TextureInfo.mipLevelSize(meta.height(), out.level) : fbo.height;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2283,8 +2230,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
|||||||
final int glId = boundTextureFor(cs, target);
|
final int glId = boundTextureFor(cs, target);
|
||||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(glId);
|
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(glId);
|
||||||
if (meta == null || level < 0 || level >= meta.levels()) return;
|
if (meta == null || level < 0 || level >= meta.levels()) return;
|
||||||
final int width = PixelOps.mipLevelSize(meta.width(), level);
|
final int width = TextureInfo.mipLevelSize(meta.width(), level);
|
||||||
final int height = PixelOps.mipLevelSize(meta.height(), level);
|
final int height = TextureInfo.mipLevelSize(meta.height(), level);
|
||||||
if (width <= 0 || height <= 0) return;
|
if (width <= 0 || height <= 0) return;
|
||||||
checkReadbackCapacity(format, type, width, height, loadPackState(cs), capacity);
|
checkReadbackCapacity(format, type, width, height, loadPackState(cs), capacity);
|
||||||
final int kind = readbackKind(format);
|
final int kind = readbackKind(format);
|
||||||
@@ -4166,8 +4113,8 @@ public class SDLGPURenderBackend extends RenderBackend {
|
|||||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(texture);
|
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(texture);
|
||||||
if (meta == null) return 0;
|
if (meta == null) return 0;
|
||||||
return switch (pname) {
|
return switch (pname) {
|
||||||
case GL11.GL_TEXTURE_WIDTH -> PixelOps.mipLevelSize(meta.width(), level);
|
case GL11.GL_TEXTURE_WIDTH -> TextureInfo.mipLevelSize(meta.width(), level);
|
||||||
case GL11.GL_TEXTURE_HEIGHT -> PixelOps.mipLevelSize(meta.height(), level);
|
case GL11.GL_TEXTURE_HEIGHT -> TextureInfo.mipLevelSize(meta.height(), level);
|
||||||
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> {
|
case GL11.GL_TEXTURE_INTERNAL_FORMAT -> {
|
||||||
final int stored = meta.glFormat();
|
final int stored = meta.glFormat();
|
||||||
yield TextureInfoCache.isGenericCompressedInternalFormat(stored) ? GL11.GL_RGBA8 : stored;
|
yield TextureInfoCache.isGenericCompressedInternalFormat(stored) ? GL11.GL_RGBA8 : stored;
|
||||||
@@ -4246,10 +4193,10 @@ public class SDLGPURenderBackend extends RenderBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static final int MAX_UNIFORM_COMPONENTS_PER_STAGE = 4096 / Float.BYTES;
|
static final int MAX_UNIFORM_COMPONENTS_PER_STAGE = 4096 / Float.BYTES;
|
||||||
static final int MAX_ARRAY_TEXTURE_LAYERS = 256;
|
public static final int MAX_ARRAY_TEXTURE_LAYERS = 256;
|
||||||
static final int MAX_TEXTURE_SIZE = 16384;
|
public static final int MAX_TEXTURE_SIZE = 16384;
|
||||||
static final int MAX_CUBE_MAP_TEXTURE_SIZE = 16384;
|
public static final int MAX_CUBE_MAP_TEXTURE_SIZE = 16384;
|
||||||
static final int MAX_3D_TEXTURE_SIZE = 2048;
|
public static final int MAX_3D_TEXTURE_SIZE = 2048;
|
||||||
/** D3D12 and Metal both cap anisotropy at 16, and every desktop Vulkan driver reports 16. */
|
/** D3D12 and Metal both cap anisotropy at 16, and every desktop Vulkan driver reports 16. */
|
||||||
static final float MAX_ANISOTROPY = 16.0f;
|
static final float MAX_ANISOTROPY = 16.0f;
|
||||||
|
|
||||||
@@ -4412,9 +4359,9 @@ public class SDLGPURenderBackend extends RenderBackend {
|
|||||||
if (texHandle == 0 || frameManager.getCommandBuffer() == 0) return;
|
if (texHandle == 0 || frameManager.getCommandBuffer() == 0) return;
|
||||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(texture);
|
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(texture);
|
||||||
if (meta == null || level >= meta.levels()) return;
|
if (meta == null || level >= meta.levels()) return;
|
||||||
final int mipW = PixelOps.mipLevelSize(meta.width(), level);
|
final int mipW = TextureInfo.mipLevelSize(meta.width(), level);
|
||||||
final int mipH = PixelOps.mipLevelSize(meta.height(), level);
|
final int mipH = TextureInfo.mipLevelSize(meta.height(), level);
|
||||||
final int mipD = PixelOps.mipLevelSize(meta.depth(), level);
|
final int mipD = TextureInfo.mipLevelSize(meta.depth(), level);
|
||||||
final int bpp = PixelOps.sdlFormatTexelBytes(meta.sdlFormat());
|
final int bpp = PixelOps.sdlFormatTexelBytes(meta.sdlFormat());
|
||||||
if (mipW <= 0 || mipH <= 0 || bpp <= 0) return;
|
if (mipW <= 0 || mipH <= 0 || bpp <= 0) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.gtnewhorizons.angelica.sdlgpu.frame;
|
package com.gtnewhorizons.angelica.sdlgpu.frame;
|
||||||
|
|
||||||
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
import com.gtnewhorizons.angelica.glsm.GLContextState;
|
||||||
|
import com.gtnewhorizons.angelica.glsm.texture.ProxyTexture;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend;
|
import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineCache;
|
import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineCache;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ColorSource;
|
import com.gtnewhorizons.angelica.sdlgpu.resource.ColorSource;
|
||||||
@@ -67,27 +68,8 @@ public final class ContextState {
|
|||||||
public final PackState packState = new PackState();
|
public final PackState packState = new PackState();
|
||||||
public final ColorSource readColor = new ColorSource();
|
public final ColorSource readColor = new ColorSource();
|
||||||
|
|
||||||
public static final class ProxyTextureState {
|
public final ProxyTexture proxyTexture = new ProxyTexture(SDLGPURenderBackend.MAX_TEXTURE_SIZE, SDLGPURenderBackend.MAX_3D_TEXTURE_SIZE,
|
||||||
public int target;
|
SDLGPURenderBackend.MAX_CUBE_MAP_TEXTURE_SIZE, SDLGPURenderBackend.MAX_ARRAY_TEXTURE_LAYERS);
|
||||||
public int width;
|
|
||||||
public int height;
|
|
||||||
public int depth;
|
|
||||||
public int internalFormat;
|
|
||||||
|
|
||||||
public void accept(int target, int width, int height, int depth, int internalFormat) {
|
|
||||||
this.target = target;
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
this.depth = depth;
|
|
||||||
this.internalFormat = internalFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reject(int target) {
|
|
||||||
accept(target, 0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final ProxyTextureState proxyTexture = new ProxyTextureState();
|
|
||||||
|
|
||||||
public static final class VAOState {
|
public static final class VAOState {
|
||||||
public final boolean[] attribEnabled = new boolean[MAX_VERTEX_ATTRIBS];
|
public final boolean[] attribEnabled = new boolean[MAX_VERTEX_ATTRIBS];
|
||||||
|
|||||||
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
package com.gtnewhorizons.angelica.sdlgpu.pipeline;
|
package com.gtnewhorizons.angelica.sdlgpu.pipeline;
|
||||||
|
|
||||||
|
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager.FrameState;
|
import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager.FrameState;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.resource.FboState;
|
import com.gtnewhorizons.angelica.sdlgpu.resource.FboState;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.resource.PixelOps;
|
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.resource.TextureOps;
|
import com.gtnewhorizons.angelica.sdlgpu.resource.TextureOps;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.shader.LogicOpFormats;
|
import com.gtnewhorizons.angelica.sdlgpu.shader.LogicOpFormats;
|
||||||
@@ -121,13 +121,13 @@ public final class LogicOpEmulator {
|
|||||||
if (fbo == null) return frameManager.getFbo0Width();
|
if (fbo == null) return frameManager.getFbo0Width();
|
||||||
final int db = drawBuffer(fbo, loc);
|
final int db = drawBuffer(fbo, loc);
|
||||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(fbo.colorGlIds[db]);
|
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(fbo.colorGlIds[db]);
|
||||||
return meta != null ? PixelOps.mipLevelSize(meta.width(), fbo.colorLevels[db]) : fbo.width;
|
return meta != null ? TextureInfo.mipLevelSize(meta.width(), fbo.colorLevels[db]) : fbo.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int targetHeight(FboState fbo, int loc) {
|
private int targetHeight(FboState fbo, int loc) {
|
||||||
if (fbo == null) return frameManager.getFbo0Height();
|
if (fbo == null) return frameManager.getFbo0Height();
|
||||||
final int db = drawBuffer(fbo, loc);
|
final int db = drawBuffer(fbo, loc);
|
||||||
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(fbo.colorGlIds[db]);
|
final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(fbo.colorGlIds[db]);
|
||||||
return meta != null ? PixelOps.mipLevelSize(meta.height(), fbo.colorLevels[db]) : fbo.height;
|
return meta != null ? TextureInfo.mipLevelSize(meta.height(), fbo.colorLevels[db]) : fbo.height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,12 +14,6 @@ import static org.lwjgl.sdl.SDLGPU.*;
|
|||||||
public final class PixelOps {
|
public final class PixelOps {
|
||||||
private PixelOps() {}
|
private PixelOps() {}
|
||||||
|
|
||||||
public static int mipLevelSize(int base, int level) {
|
|
||||||
if (base <= 0 || level < 0) return 0;
|
|
||||||
if (level >= Integer.SIZE - 1) return 1;
|
|
||||||
return Math.max(1, base >> level);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int defaultMipLevels(int width, int height) {
|
public static int defaultMipLevels(int width, int height) {
|
||||||
final int max = Math.max(1, Math.max(width, height));
|
final int max = Math.max(1, Math.max(width, height));
|
||||||
return Math.max(1, 32 - Integer.numberOfLeadingZeros(max));
|
return Math.max(1, 32 - Integer.numberOfLeadingZeros(max));
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.gtnewhorizons.angelica.sdlgpu.resource;
|
package com.gtnewhorizons.angelica.sdlgpu.resource;
|
||||||
|
|
||||||
|
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.device.Device;
|
import com.gtnewhorizons.angelica.sdlgpu.device.Device;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.device.Submits;
|
import com.gtnewhorizons.angelica.sdlgpu.device.Submits;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||||
@@ -156,11 +157,11 @@ public final class TextureOps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int levelWidth(ResourceManager.TextureMeta meta, int level) {
|
public static int levelWidth(ResourceManager.TextureMeta meta, int level) {
|
||||||
return meta != null ? PixelOps.mipLevelSize(meta.width(), level) : 0;
|
return meta != null ? TextureInfo.mipLevelSize(meta.width(), level) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int levelHeight(ResourceManager.TextureMeta meta, int level) {
|
public static int levelHeight(ResourceManager.TextureMeta meta, int level) {
|
||||||
return meta != null ? PixelOps.mipLevelSize(meta.height(), level) : 0;
|
return meta != null ? TextureInfo.mipLevelSize(meta.height(), level) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void copyFromDefaultFramebuffer(ContextState st, int destGlId, long destTex, int level, int xoffset, int yoffset, int x, int y, int width, int height) {
|
private void copyFromDefaultFramebuffer(ContextState st, int destGlId, long destTex, int level, int xoffset, int yoffset, int x, int y, int width, int height) {
|
||||||
|
|||||||
+13
-12
@@ -1,5 +1,6 @@
|
|||||||
package com.gtnewhorizons.angelica.sdlgpu;
|
package com.gtnewhorizons.angelica.sdlgpu;
|
||||||
|
|
||||||
|
import com.gtnewhorizons.angelica.glsm.texture.ProxyTexture;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -47,11 +48,11 @@ class ProxyTextureLimitsTest {
|
|||||||
assertEquals(2048, SDLGPURenderBackend.deviceLimit(GL12.GL_MAX_3D_TEXTURE_SIZE));
|
assertEquals(2048, SDLGPURenderBackend.deviceLimit(GL12.GL_MAX_3D_TEXTURE_SIZE));
|
||||||
assertEquals(16384, SDLGPURenderBackend.deviceLimit(GL13.GL_MAX_CUBE_MAP_TEXTURE_SIZE));
|
assertEquals(16384, SDLGPURenderBackend.deviceLimit(GL13.GL_MAX_CUBE_MAP_TEXTURE_SIZE));
|
||||||
|
|
||||||
assertEquals(16384, SDLGPURenderBackend.proxyDimensionLimit(GL11.GL_PROXY_TEXTURE_2D));
|
assertEquals(16384, SdlTestRig.contextState().proxyTexture.dimensionLimit(GL11.GL_PROXY_TEXTURE_2D));
|
||||||
assertEquals(16384, SDLGPURenderBackend.proxyDimensionLimit(GL11.GL_PROXY_TEXTURE_1D));
|
assertEquals(16384, SdlTestRig.contextState().proxyTexture.dimensionLimit(GL11.GL_PROXY_TEXTURE_1D));
|
||||||
assertEquals(2048, SDLGPURenderBackend.proxyDimensionLimit(GL12.GL_PROXY_TEXTURE_3D));
|
assertEquals(2048, SdlTestRig.contextState().proxyTexture.dimensionLimit(GL12.GL_PROXY_TEXTURE_3D));
|
||||||
assertEquals(16384, SDLGPURenderBackend.proxyDimensionLimit(GL13.GL_PROXY_TEXTURE_CUBE_MAP));
|
assertEquals(16384, SdlTestRig.contextState().proxyTexture.dimensionLimit(GL13.GL_PROXY_TEXTURE_CUBE_MAP));
|
||||||
assertEquals(16384, SDLGPURenderBackend.proxyDimensionLimit(GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY));
|
assertEquals(16384, SdlTestRig.contextState().proxyTexture.dimensionLimit(GL40.GL_PROXY_TEXTURE_CUBE_MAP_ARRAY));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -99,13 +100,13 @@ class ProxyTextureLimitsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void levelZeroEquivalentSizeRejectsOverflowAndNonsense() {
|
void levelZeroEquivalentSizeRejectsOverflowAndNonsense() {
|
||||||
assertEquals(4, SDLGPURenderBackend.levelZeroEquivalentSize(4, 0));
|
assertEquals(4, ProxyTexture.levelZeroEquivalentSize(4, 0));
|
||||||
assertEquals(32, SDLGPURenderBackend.levelZeroEquivalentSize(4, 3));
|
assertEquals(32, ProxyTexture.levelZeroEquivalentSize(4, 3));
|
||||||
assertEquals(0, SDLGPURenderBackend.levelZeroEquivalentSize(4, -1));
|
assertEquals(0, ProxyTexture.levelZeroEquivalentSize(4, -1));
|
||||||
assertEquals(0, SDLGPURenderBackend.levelZeroEquivalentSize(0, 2));
|
assertEquals(0, ProxyTexture.levelZeroEquivalentSize(0, 2));
|
||||||
assertEquals(0, SDLGPURenderBackend.levelZeroEquivalentSize(4, 31));
|
assertEquals(0, ProxyTexture.levelZeroEquivalentSize(4, 31));
|
||||||
assertEquals(0, SDLGPURenderBackend.levelZeroEquivalentSize(4, 30));
|
assertEquals(0, ProxyTexture.levelZeroEquivalentSize(4, 30));
|
||||||
assertEquals(1 << 30, SDLGPURenderBackend.levelZeroEquivalentSize(1, 30));
|
assertEquals(1 << 30, ProxyTexture.levelZeroEquivalentSize(1, 30));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.gtnewhorizons.angelica.sdlgpu;
|
package com.gtnewhorizons.angelica.sdlgpu;
|
||||||
|
|
||||||
|
import com.gtnewhorizons.angelica.glsm.texture.TextureInfo;
|
||||||
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
@@ -81,7 +82,7 @@ class ProxyTextureTargetTest {
|
|||||||
@ParameterizedTest(name = "{0}")
|
@ParameterizedTest(name = "{0}")
|
||||||
@MethodSource("levelSizeCases")
|
@MethodSource("levelSizeCases")
|
||||||
void levelSizeMatchesExpectation(String label, int base, int level, int expected) {
|
void levelSizeMatchesExpectation(String label, int base, int level, int expected) {
|
||||||
assertEquals(expected, SDLGPURenderBackend.proxyLevelSize(base, level), label);
|
assertEquals(expected, TextureInfo.mipLevelSize(base, level), label);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in new issue
Block a user