From 3978be161d5743942674db61c52a0997e6dde3f8 Mon Sep 17 00:00:00 2001 From: Jason Mitchell Date: Sat, 10 Oct 2026 17:31:02 -0700 Subject: [PATCH] Answer legacy GL queries from tracked state (#2247) --- .github/workflows/build-and-test.yml | 2 + .../gtnewhorizons/angelica/glsm/Feature.java | 17 +- .../angelica/glsm/FeedbackManager.java | 12 + .../angelica/glsm/GLContextState.java | 10 +- .../angelica/glsm/GLStateManager.java | 806 +++++++++++----- .../angelica/glsm/LegacyQueries.java | 167 ++++ .../gtnewhorizons/angelica/glsm/StateSet.java | 4 + .../angelica/glsm/ffp/VAOManager.java | 542 +++++++++-- .../glsm/redirect/GLSMRedirector.java | 14 +- .../angelica/glsm/states/PointState.java | 24 +- .../glsm/states/TextureUnitArray.java | 9 + .../glsm/ArrayUniformUploadGLTest.java | 32 +- .../angelica/glsm/CaptureGateTest.java | 30 +- .../glsm/CompatShaderTransformerTest.java | 518 ++++------- .../DisplayListCompilationParityTest.java | 7 +- .../glsm/DisplayListCompilationTest.java | 75 +- .../glsm/DisplayListIdAllocatorTest.java | 37 +- .../DisplayListMatrixRestorationTest.java | 4 +- .../angelica/glsm/DisplayListTestHelper.java | 61 ++ .../angelica/glsm/FeedbackManagerTest.java | 154 +--- .../GLSM_BlendFuncUnknownState_GLTest.java | 7 - .../glsm/GLSM_BlendUnknownState_UnitTest.java | 60 +- .../glsm/GLSM_ClientArrays_GLTest.java | 672 ++++++++++++++ .../glsm/GLSM_CoreRemovedCaps_GLTest.java | 7 - .../GLSM_DeferredTextureDelete_GLTest.java | 62 ++ .../glsm/GLSM_DirtyTracking_UnitTest.java | 59 +- ...M_DisplayList_FormatOptimization_Test.java | 106 +-- .../GLSM_DisplayList_PixelStore_Test.java | 77 +- ...playList_RawTessellatorTransform_Test.java | 32 +- ..._DisplayList_TransformCollapsing_Test.java | 403 +------- .../glsm/GLSM_DisplayList_UnitTest.java | 330 ++----- .../angelica/glsm/GLSM_GLU_UnitTest.java | 93 +- .../GLSM_ImageUnitBindingCache_UnitTest.java | 35 +- .../glsm/GLSM_LegacyStackQueries_GLTest.java | 770 +++++++++++----- .../angelica/glsm/GLSM_Lighting_UnitTest.java | 71 +- .../glsm/GLSM_LineStipple_GLTest.java | 49 +- .../glsm/GLSM_PerThread_UnitTest.java | 18 +- .../angelica/glsm/GLSM_PushPop_UnitTest.java | 79 +- .../glsm/GLSM_RetainedState_GLTest.java | 15 +- .../glsm/GLSM_SamplerBinding_GLTest.java | 6 - .../glsm/GLSM_ServerSideState_UnitTest.java | 140 ++- .../GLSM_SplashCachingCoverage_UnitTest.java | 30 +- .../GLSM_SplashCompleteLatch_UnitTest.java | 10 +- .../angelica/glsm/GLSM_StateSet_GLTest.java | 857 +++++++----------- ...SM_TextureBindingTargetCache_UnitTest.java | 13 - .../GLSM_TextureUploadGeneration_GLTest.java | 2 + .../angelica/glsm/GLSM_VAO_UnitTest.java | 22 +- .../glsm/GLSM_ValueStackCow_GLTest.java | 3 + .../ImmediateModeThreadIsolationTest.java | 46 +- .../glsm/InstancedQuadDrawGLTest.java | 72 +- .../angelica/glsm/QuadConverterTest.java | 7 - .../angelica/glsm/ReadbackGLTest.java | 78 +- .../glsm/dsa/DSAUnsupportedBindingTest.java | 4 +- .../glsm/ffp/FFPVertexLightingGLTest.java | 84 +- .../glsm/ffp/FfpUniformBlockDedupTest.java | 8 +- .../glsm/ffp/InstancedFFPDrawGLTest.java | 3 +- .../angelica/glsm/ffp/LogicOpGLTest.java | 2 - .../angelica/glsm/ffp/WideLineClipGLTest.java | 20 +- .../glsm/loading/ModRedirectorTest.java | 41 +- .../glsm/profiling/TracyOptionsTest.java | 44 +- .../IndexedDrawBatchBuilderTest.java | 24 - .../IndexedDrawDeleteSafetyTest.java | 81 +- .../glsm/recording/IndexedDrawReplayTest.java | 38 +- .../RecordingDataStructuresTest.java | 33 - .../commands/AttribLayoutKeyTest.java | 98 +- .../glsm/redirect/GLSMRedirectorTest.java | 166 +--- .../gtnewhorizons/angelica/util/GLSMUtil.java | 56 +- .../angelica/glsm/GLSMCoreExtension.java | 11 +- .../angelica/glsm/GlTestSupport.java | 82 ++ .../angelica/glsm/LegacyQueryFixture.java | 84 ++ .../angelica/glsm/ffp/FfpFixture.java | 25 +- .../lwjgl3/Lwjgl3GLRenderBackend.java | 3 + .../angelica/sdlgpu/SDLGPURenderBackend.java | 2 +- .../angelica/sdlgpu/DrawBuffersLimitTest.java | 3 +- .../sdlgpu/ImageAccessUsageMappingTest.java | 39 +- .../angelica/sdlgpu/RealDeviceExtension.java | 51 ++ .../sdlgpu/SDLGPUBufferQueryTest.java | 12 - .../angelica/sdlgpu/SdlTestRig.java | 46 +- .../angelica/sdlgpu/UboShadowSubDataTest.java | 7 +- .../sdlgpu/UniformNegativeLocationTest.java | 9 +- .../compat/Lwjgl3GLCapabilitiesShimTest.java | 28 +- .../compute/ChunkCullSpirvCompileTest.java | 7 +- .../sdlgpu/compute/ImageAtomicsGpuTest.java | 35 - .../device/FenceProbeRealDeviceTest.java | 18 +- .../device/VideoDriverRecoveryTest.java | 43 +- .../sdlgpu/frame/AcquireAffinityTest.java | 31 +- .../angelica/sdlgpu/frame/PresenterTest.java | 5 +- .../glsm/GlsmSdlAttachmentClearTest.java | 111 +-- .../glsm/GlsmSdlBlendConstantsTest.java | 31 +- .../GlsmSdlBufferMappingLifetimeTest.java | 30 +- .../sdlgpu/glsm/GlsmSdlCopyBlitTest.java | 115 +-- .../glsm/GlsmSdlDegenerateDrawTest.java | 29 +- .../sdlgpu/glsm/GlsmSdlFbo0ViewportTest.java | 30 +- .../angelica/sdlgpu/glsm/GlsmSdlFfpTest.java | 78 +- .../sdlgpu/glsm/GlsmSdlFrameExtension.java | 8 +- .../sdlgpu/glsm/GlsmSdlHeadlessRig.java | 115 ++- .../glsm/GlsmSdlIndexedBlendRestoreTest.java | 12 +- .../glsm/GlsmSdlInstancedConstantTest.java | 23 +- .../glsm/GlsmSdlLWJGLServiceRoutingTest.java | 1 - .../sdlgpu/glsm/GlsmSdlLegacyQueryTest.java | 187 ++++ .../sdlgpu/glsm/GlsmSdlLogicOpTest.java | 27 +- .../sdlgpu/glsm/GlsmSdlMipAttachmentTest.java | 8 +- .../glsm/GlsmSdlOffContextCreationTest.java | 14 - .../glsm/GlsmSdlOutOfFrameExtension.java | 14 +- .../glsm/GlsmSdlOutOfFrameReadbackTest.java | 22 +- .../glsm/GlsmSdlProxyTextureProbeTest.java | 65 +- .../glsm/GlsmSdlReadbackMatrixTest.java | 90 +- .../sdlgpu/glsm/GlsmSdlReadbackTest.java | 6 - .../glsm/GlsmSdlSamplerBindingTest.java | 6 - .../sdlgpu/glsm/GlsmSdlSplashTargetTest.java | 17 +- .../sdlgpu/glsm/GlsmSdlStreamingRingTest.java | 6 - .../glsm/GlsmSdlTextureBindingQueryTest.java | 6 - .../glsm/GlsmSdlTextureStagingTest.java | 5 +- .../glsm/GlsmSdlUnalignedAttribTest.java | 41 +- .../glsm/GlsmSdlVanillaTextureTest.java | 27 +- .../glsm/GlsmSdlVoxelRegionBindTest.java | 6 - .../sdlgpu/glsm/GlsmSdlWideLineTest.java | 25 +- .../sdlgpu/pipeline/AttachmentClearTest.java | 30 +- .../pipeline/BlendConstantsGuardTest.java | 73 +- .../pipeline/ClearValueLayoutHashTest.java | 7 - .../pipeline/DepthStencilReadbackTest.java | 19 +- .../sdlgpu/pipeline/PipelineCacheKeyTest.java | 157 +--- .../PipelineCacheVertexFormatTest.java | 139 +-- .../PipelineCacheVertexInputTest.java | 160 ++-- .../pipeline/PrimitiveRestartScanTest.java | 138 +-- .../sdlgpu/pipeline/ScissorClampTest.java | 84 +- .../sdlgpu/pipeline/StencilMaskGpuTest.java | 29 +- .../resource/BgraStorageChannelTest.java | 98 +- .../sdlgpu/resource/BlitCopyDecisionTest.java | 44 +- .../resource/BufferContentsDefinedTest.java | 12 +- .../sdlgpu/resource/BufferGlParamsTest.java | 16 +- .../resource/BufferSizeGpuExactnessTest.java | 7 +- .../resource/ClearTexImageFormatTest.java | 9 +- .../resource/ColorContentLifetimeTest.java | 3 +- .../resource/CopyAllMipsVolumeTest.java | 49 - .../CopyMappedFromDataBoundsTest.java | 51 +- .../CopyTexSubImageFromFbo0GpuTest.java | 6 +- .../CrossThreadUploadCBIsolationTest.java | 106 +-- .../sdlgpu/resource/DepthStencilBitsTest.java | 15 - .../sdlgpu/resource/PixelFormatBitsTest.java | 16 +- .../ReleasePersistentStagingTest.java | 61 +- .../ResourceManagerFormatMappingTest.java | 108 +-- .../ResourceManagerUsageCombinationTest.java | 36 +- .../TextureReleaseUploadOrderTest.java | 9 +- .../resource/TextureSamplerStateTest.java | 46 +- .../resource/TransferRetireSeqTest.java | 10 +- .../resource/TransferThreadDeferFreeTest.java | 35 +- .../TransferThreadWakePolicyTest.java | 29 +- .../resource/UploadArenaGpuRoundTripTest.java | 14 +- .../UploadCBAutoFlushThresholdTest.java | 70 +- .../UploadCBBatchFillCycleSafetyTest.java | 82 +- .../sampler/SamplerBindGenerationTest.java | 5 +- .../sdlgpu/sampler/SamplerCacheTest.java | 13 - .../shader/CanonicalBlockLayoutTest.java | 12 +- .../shader/ClearImage3DReflectionTest.java | 7 +- .../sdlgpu/shader/ComputeSpirvRemapTest.java | 18 +- .../ComputeStorageImageReflectionTest.java | 7 +- .../shader/FontFilterVertexSamplerTest.java | 8 +- .../shader/FragmentOutputReflectionTest.java | 92 +- .../FragmentStorageTextureReadGpuTest.java | 23 +- .../shader/GraphicsBindingMapSsboTest.java | 14 +- .../sdlgpu/shader/LogicOpLoweringTest.java | 5 +- .../shader/PerFrameBlockInjectorTest.java | 24 +- .../PerFrameBlockLocationRoutingTest.java | 7 +- .../shader/PerFrameBlockReadOnlyTest.java | 8 +- .../shader/PerFrameBlockReflectionTest.java | 8 +- .../PrewarmSingleParseEquivalenceTest.java | 366 +++----- .../PrewarmTreeHandoffEquivalenceTest.java | 163 ++-- .../ReadOnlyStorageTextureDescriptorTest.java | 7 +- .../shader/SDLGPUUniformPackingTest.java | 98 +- .../SdlSamplerStripperPreprocessorTest.java | 189 ++-- .../sdlgpu/shader/ShaderCompilationTest.java | 130 --- .../shader/ShaderStageSerializerTest.java | 6 +- .../sdlgpu/shader/SpirvTestShaders.java | 17 + .../sdlgpu/shader/SpvcArrayStrideTest.java | 149 +-- .../sdlgpu/shader/SpvcReflectionTest.java | 181 ---- .../sdlgpu/shader/Std140WriterTest.java | 71 +- .../sdlgpu/shader/UniformBoolRemapTest.java | 32 +- .../shader/UnusedSamplerReflectionTest.java | 6 +- .../shader/cross/LocalSizeParseTest.java | 48 +- .../sdlgpu/shader/dxbc/DxbcCompileTest.java | 90 +- .../shader/dxbc/DxbcHlslSpacesTest.java | 60 +- .../msl/FragmentStorageTextureSlotTest.java | 17 +- .../shader/msl/MslCrossCompileTest.java | 106 +-- .../sdlgpu/threading/AutoVaoBindTest.java | 57 -- .../ContextStateNativeBufsAllocatedTest.java | 17 - .../threading/PerThreadContextStateTest.java | 55 -- .../PipelineCacheConcurrentBuildTest.java | 24 +- .../angelica/api/GLRedirectsTest.java | 22 +- .../client/ChatLineFormattedCacheTest.java | 66 -- .../client/font/ColorCodeUtilsTest.java | 45 +- .../client/font/FontBatchMatrixTest.java | 48 - .../client/font/FontEffectRegistryTest.java | 5 - .../client/font/FontFilterLightmapGLTest.java | 21 +- .../client/font/TextDrawStateGLTest.java | 18 +- .../rendering/DeferredDrawBatcherTest.java | 163 +--- .../commands/AngelicaCommandTest.java | 29 +- .../angelica/config/GLVersionChoiceTest.java | 32 +- .../angelica/debug/flyby/FlybySceneTest.java | 49 +- .../debug/profiling/AsprofRecorderTest.java | 59 +- .../iris/IrisDisplayListStateTest.java | 20 +- .../angelica/rendering/PacerCoreSimTest.java | 40 +- .../angelica/rendering/PacerCoreTest.java | 141 ++- .../angelica/rendering/PacerSleeperTest.java | 61 +- .../angelica/rendering/PacerTestClock.java | 54 ++ .../rendering/PlayerReflectionLayoutTest.java | 37 +- .../PlayerReflectionSsboMappingTest.java | 28 +- .../PlayerReflectionTestSupport.java | 32 + .../rendering/ReducerStateMachineTest.java | 63 +- .../rendering/VertexAttributeLayoutTest.java | 11 - .../celeritas/CameraTransformReuseTest.java | 32 +- .../celeritas/DeferredSectionMeshTest.java | 50 +- .../celeritas/SectionFrustumGateTest.java | 14 - .../celeritas/TerrainShaderLinkTest.java | 11 +- .../TreeSortCameraBookkeepingGLTest.java | 8 +- .../ThreadedCanRenderOffThreadTest.java | 9 +- .../culling/CullingShaderCompileTest.java | 14 +- .../culling/FrustumExtractorTest.java | 6 - .../culling/IndirectCullDrawParityTest.java | 64 +- .../culling/MultiDrawCompatStompTest.java | 14 +- .../culling/SectionMetaBufferTest.java | 59 +- .../culling/SectionMetaSlotLifecycleTest.java | 55 -- .../culling/ShadowSnapshotBlitTest.java | 130 ++- .../culling/TerrainPassPreparationTest.java | 44 +- .../rendering/tesr/BatchStateGuardGLTest.java | 80 +- .../rendering/tesr/ModelBoxCaptureTest.java | 10 +- .../tesr/RetainedTesrGroupsGLTest.java | 156 ++-- .../tesr/RetainedTesrGroupsTest.java | 86 +- .../tesr/RetainedTesrTestSupport.java | 104 +++ .../tesr/StateSetOverrideGLTest.java | 123 +-- .../rendering/tesr/TesrAnchorMathTest.java | 10 - .../rendering/tesr/TesrLayerStateTest.java | 57 +- .../voxelization/ShadowVoxelizerTest.java | 46 +- .../textures/atlas/AtlasPackPolicyTest.java | 34 +- .../textures/atlas/MipmapSafetyTest.java | 34 +- .../utils/ConcurrentTileEntityMapTest.java | 22 +- .../angelica/utils/SpritePaddingTest.java | 11 +- .../util/StringUtilsTest.java | 71 +- .../impl/AngelicaBufferSourceTest.java | 18 +- .../impl/BufferSourceProbe.java | 23 + .../impl/SegmentedBufferBuilderTest.java | 25 +- .../iris/IrisProgramSetWarmupTest.java | 4 +- .../iris/PackSettingsFingerprintTest.java | 16 +- .../program/ProgramBindingRegressionTest.java | 16 +- .../uniform/ManifestCollectingHolderTest.java | 5 - .../AttributeTransformerLinkGLTest.java | 14 +- .../iris/pipeline/IrisInstancingGLTest.java | 25 +- .../iris/pipeline/MvBuiltinScanTest.java | 45 +- .../AttributeTransformerInstancedTest.java | 35 +- .../CompositeVertexWriteExtractionTest.java | 18 +- .../DHGenericAttributeLocationTest.java | 18 +- .../LocalZeroInitTransformerTest.java | 82 +- .../transform/RwImageStoreExtractorTest.java | 258 +++--- .../SamplerAliasDeduplicatorTest.java | 53 +- .../SamplerToStorageImageRewriterTest.java | 208 ++--- .../ShaderTransformerDiskCacheTest.java | 18 +- .../transform/ShaderTransformerTest.java | 189 ++-- .../transform/TransformTestSupport.java | 60 ++ .../transform/VertexReplayExtractionTest.java | 24 +- .../ShadowFrustumIntersectAabTest.java | 8 - .../PerFrameUniformBlockHarvesterTest.java | 5 - .../angelica/tracy/TracyBindingSmokeTest.java | 15 +- .../angelica/tracy/TracyPortsTest.java | 2 + .../angelica/tracy/ZoneStackTest.java | 7 - 264 files changed, 7276 insertions(+), 9375 deletions(-) create mode 100644 glsm/src/main/java/com/gtnewhorizons/angelica/glsm/LegacyQueries.java create mode 100644 glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ClientArrays_GLTest.java create mode 100644 glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DeferredTextureDelete_GLTest.java create mode 100644 glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/GlTestSupport.java create mode 100644 glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/LegacyQueryFixture.java create mode 100644 sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/RealDeviceExtension.java delete mode 100644 sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/compute/ImageAtomicsGpuTest.java create mode 100644 sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLegacyQueryTest.java delete mode 100644 sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CopyAllMipsVolumeTest.java delete mode 100644 sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ShaderCompilationTest.java delete mode 100644 sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SpvcReflectionTest.java delete mode 100644 sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/AutoVaoBindTest.java delete mode 100644 src/test/java/com/gtnewhorizons/angelica/client/ChatLineFormattedCacheTest.java delete mode 100644 src/test/java/com/gtnewhorizons/angelica/client/font/FontBatchMatrixTest.java create mode 100644 src/test/java/com/gtnewhorizons/angelica/rendering/PacerTestClock.java create mode 100644 src/test/java/com/gtnewhorizons/angelica/rendering/PlayerReflectionTestSupport.java create mode 100644 src/test/java/com/gtnewhorizons/angelica/rendering/tesr/RetainedTesrTestSupport.java create mode 100644 src/test/java/net/coderbot/iris/pipeline/transform/TransformTestSupport.java diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 3ee2f686f..041a6c298 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -10,4 +10,6 @@ on: jobs: build-and-test: uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master + with: + disable-spotless-pr: true secrets: inherit diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/Feature.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/Feature.java index 05ca1b7d2..614374918 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/Feature.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/Feature.java @@ -96,6 +96,7 @@ public class Feature { , GLStateManager.getSecondaryColor() // Current secondary color , ShaderManager.getNormalStack() // Current normal vector , ShaderManager.getTexCoordStack() // Current texture coordinates + , GLStateManager.getEdgeFlagState() // Current color index // Current raster position // GL_CURRENT_RASTER_POSITION_VALID flag @@ -158,6 +159,8 @@ public class Feature { , GLStateManager.getPolygonOffsetPointState() , GLStateManager.getPolygonOffsetLineState() , GLStateManager.getPolygonOffsetFillState() + , GLStateManager.getPointSpriteState() + , GLStateManager.getVertexProgramTwoSideState() , GLStateManager.getPolygonSmoothState() , GLStateManager.getPolygonStippleState() , GLStateManager.getRescaleNormalState() @@ -178,6 +181,7 @@ public class Feature { enableBits.add(GLStateManager.getTextures().getTexture1DStates(i)); enableBits.add(GLStateManager.getTextures().getTextureUnitStates(i)); enableBits.add(GLStateManager.getTextures().getTexture3DStates(i)); + enableBits.add(GLStateManager.getTextures().getTextureCubeMapStates(i)); } // Flags GL_TEXTURE_GEN_x where x is S, T, R, or Q @@ -216,13 +220,7 @@ public class Feature { // ^^ GL_FOG_MODE value )); attribToFeatures.put(GL11.GL_HINT_BIT, ImmutableSet.of( - // GL_PERSPECTIVE_CORRECTION_HINT setting - // GL_POINT_SMOOTH_HINT setting - // GL_LINE_SMOOTH_HINT setting - // GL_POLYGON_SMOOTH_HINT setting - // GL_FOG_HINT setting - // GL_GENERATE_MIPMAP_HINT setting - // GL_TEXTURE_COMPRESSION_HINT setting + GLStateManager.getHintState() )); attribToFeatures.put(GL11.GL_LIGHTING_BIT, ImmutableSet.of( GLStateManager.getColorMaterial() @@ -286,6 +284,7 @@ public class Feature { )); attribToFeatures.put(GL11.GL_POINT_BIT, ImmutableSet.of( GLStateManager.getPointSmoothState() + , GLStateManager.getPointSpriteState() , GLStateManager.getPointState() )); attribToFeatures.put(GL11.GL_POLYGON_BIT, ImmutableSet.of( @@ -328,6 +327,10 @@ public class Feature { for(int i = 0 ; i < GLStateManager.MAX_TEXTURE_UNITS; i++) { textureAttribs.add(GLStateManager.getTextures().getTextureUnitBindings(i)); textureAttribs.add(GLStateManager.getTextures().getTexEnvState(i)); + textureAttribs.add(GLStateManager.getTextures().getTexture1DStates(i)); + textureAttribs.add(GLStateManager.getTextures().getTextureUnitStates(i)); + textureAttribs.add(GLStateManager.getTextures().getTexture3DStates(i)); + textureAttribs.add(GLStateManager.getTextures().getTextureCubeMapStates(i)); } // Enable bits GL_TEXTURE_GEN_x where x is S, T, R, or Q diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/FeedbackManager.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/FeedbackManager.java index 6ea8c5196..86b91f89a 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/FeedbackManager.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/FeedbackManager.java @@ -1,5 +1,6 @@ package com.gtnewhorizons.angelica.glsm; +import com.gtnewhorizons.angelica.glsm.ffp.VAOManager; import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder; import com.gtnewhorizons.angelica.glsm.states.ViewportState; import org.joml.Matrix4f; @@ -141,6 +142,7 @@ public class FeedbackManager { public static void processDrawArrays(int mode, int first, int count) { computeMVP(); final GLContextState glCtx = GLStateManager.ctx(); + if (!glCtx.vaos.isAttribEnabled(0)) return; final int stride = positionStride(glCtx); final long posOffset = positionOffset(glCtx); final int byteCount = count * stride; @@ -154,6 +156,7 @@ public class FeedbackManager { public static void processDrawElements(int mode, int indexCount, int type, long eboOffset) { computeMVP(); final GLContextState glCtx = GLStateManager.ctx(); + if (!glCtx.vaos.isAttribEnabled(0)) return; final int stride = positionStride(glCtx); final int elementSize = GLTypes.sizeBytes(type); @@ -210,6 +213,7 @@ public class FeedbackManager { private static void processDrawElementsImpl(int mode, int count, IndexReader reader) { computeMVP(); final GLContextState glCtx = GLStateManager.ctx(); + if (!glCtx.vaos.isAttribEnabled(0)) return; final int stride = positionStride(glCtx); int minIdx = Integer.MAX_VALUE, maxIdx = Integer.MIN_VALUE; @@ -261,6 +265,14 @@ public class FeedbackManager { } private static void readPositionBuffer(long offset, ByteBuffer destination, GLContextState glCtx) { + final VAOManager.Attrib position = glCtx.vaos.attrib(0); + if (position != null && position.clientPointer != null) { + final ByteBuffer source = position.clientPointer.duplicate(); + source.position(source.position() + (int) offset); + if (source.remaining() > destination.remaining()) source.limit(source.position() + destination.remaining()); + destination.duplicate().put(source); + return; + } final int positionVbo = glCtx.vaos.attribVBO(0); final int previousVbo = glCtx.boundVBO; diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLContextState.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLContextState.java index 387a05f16..2cc3614ef 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLContextState.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLContextState.java @@ -47,6 +47,7 @@ import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL14; +import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL30; import java.nio.DoubleBuffer; @@ -169,6 +170,9 @@ public class GLContextState { public final BooleanStateStack lineSmoothState = track(new BooleanStateStack(GL11.GL_LINE_SMOOTH)); public final BooleanStateStack lineStippleState = track(new BooleanStateStack(GL11.GL_LINE_STIPPLE, false, true)); public final BooleanStateStack pointSmoothState = track(new BooleanStateStack(GL11.GL_POINT_SMOOTH, false, true)); + public final BooleanStateStack pointSpriteState = track(new BooleanStateStack(GL20.GL_POINT_SPRITE, false, true)); + public final BooleanStateStack vertexProgramTwoSideState = track(new BooleanStateStack(GL20.GL_VERTEX_PROGRAM_TWO_SIDE, false, true)); + public final BooleanStateStack edgeFlagState = track(new BooleanStateStack(GL11.GL_EDGE_FLAG, true, true)); public final BooleanStateStack polygonSmoothState = track(new BooleanStateStack(GL11.GL_POLYGON_SMOOTH)); public final BooleanStateStack polygonStippleState = track(new BooleanStateStack(GL11.GL_POLYGON_STIPPLE, false, true)); public final BooleanStateStack multisampleState = track(new BooleanStateStack(GL13.GL_MULTISAMPLE, true)); @@ -182,6 +186,7 @@ public class GLContextState { public final BooleanStateStack polygonOffsetFillState = track(new BooleanStateStack(GL11.GL_POLYGON_OFFSET_FILL)); public final LineStateStack lineState = member(new LineStateStack(StackIdAllocator.nextId())); public final PointStateStack pointState = member(new PointStateStack(StackIdAllocator.nextId())); + public final IntegerStateStack hintState = member(new IntegerStateStack(0, StackIdAllocator.nextId()).restoreBit(StateSet.R_HINT)); public final PolygonStateStack polygonState = member(new PolygonStateStack(StackIdAllocator.nextId())); public final StencilStateStack stencilState = member(new StencilStateStack(StackIdAllocator.nextId())); public int stencilBitMask = 0xFFFFFFFF; @@ -238,10 +243,9 @@ public class GLContextState { public final PixelStoreState[] clientAttribSavedPixelPack = new PixelStoreState[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; public final int[] clientAttribSavedUnpackBuffer = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; public final int[] clientAttribSavedPackBuffer = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; - public final int[] clientAttribSavedTextureUnit = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; - public final int[] clientAttribSavedVertexFlags = new int[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; - public final boolean[] clientAttribSavedVertexArray = new boolean[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; + public final VAOManager.ClientArrays[] clientAttribSavedArrays = new VAOManager.ClientArrays[GLStateManager.CLIENT_ATTRIB_STACK_DEPTH]; public int clientAttribStackPointer = 0; + public float maxPointSize = Float.NaN; public int restoreChangedMask; public long restoreUnitChangedMask; public final BlendState vanillaBlendBefore = new BlendState(); diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java index 0aaba4c95..c982db92b 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/GLStateManager.java @@ -330,6 +330,7 @@ public class GLStateManager { public static final int MAX_CLIP_PLANES = 8; public static final int MAX_TEXTURE_UNITS = RENDER_BACKEND.getInteger(GL20.GL_MAX_TEXTURE_IMAGE_UNITS); public static final int MAX_LEGACY_TEXTURE_UNITS = Math.min(VertexKey.MAX_UNITS, MAX_TEXTURE_UNITS); + public static final int MAX_TEXTURE_COORDS = Math.min(VAOManager.TEX_COORD_UNITS, MAX_TEXTURE_UNITS); public static final GLFeatureSet HAS_MULTIPLE_SET = new GLFeatureSet(); @@ -405,6 +406,7 @@ public class GLStateManager { // Deferred texture deletion: names kept valid until glGenTextures recycles them. // Emulates Mesa/compat-profile behavior where delete-then-bind doesn't error. private static final IntOpenHashSet deferredDeleteTextures = new IntOpenHashSet(); + private static final IntOpenHashSet unshrinkableTextures = new IntOpenHashSet(); private static final IntOpenHashSet writeMappedBuffers = new IntOpenHashSet(); @Getter private static Vendor VENDOR; @@ -515,6 +517,18 @@ public class GLStateManager { } } + private static float maxPointSize(GLContextState glCtx) { + if (Float.isNaN(glCtx.maxPointSize)) { + final FloatBuffer range = glCtx.queryScratch; + range.clear(); + RENDER_BACKEND.getFloat(GL11.GL_POINT_SIZE_RANGE, range); + final float smooth = range.get(1); + RENDER_BACKEND.getFloat(GL12.GL_ALIASED_POINT_SIZE_RANGE, range); + glCtx.maxPointSize = Math.max(smooth, range.get(1)); + } + return glCtx.maxPointSize; + } + private static > T mod(T s) { s.beforeModify(); return s; @@ -640,6 +654,7 @@ public class GLStateManager { .addFeature(GL11.GL_COLOR_WRITEMASK) .addFeature(GL11.GL_CURRENT_COLOR) .addFeature(GL11.GL_CURRENT_NORMAL) + .addFeature(GL14.GL_POINT_DISTANCE_ATTENUATION) .addFeature(GL14.GL_CURRENT_SECONDARY_COLOR) .addFeature(GL11.GL_CURRENT_RASTER_COLOR) .addFeature(GL11.GL_CURRENT_RASTER_POSITION) @@ -831,6 +846,8 @@ public class GLStateManager { case GL13.GL_MULTISAMPLE -> glCtx.multisampleState.enable(); case GL11.GL_NORMALIZE -> glCtx.normalizeState.enable(); case GL11.GL_POINT_SMOOTH -> glCtx.pointSmoothState.enable(); + case GL20.GL_POINT_SPRITE -> glCtx.pointSpriteState.enable(); + case GL20.GL_VERTEX_PROGRAM_TWO_SIDE -> glCtx.vertexProgramTwoSideState.enable(); case GL11.GL_POLYGON_OFFSET_POINT -> glCtx.polygonOffsetPointState.enable(); case GL11.GL_POLYGON_OFFSET_LINE -> glCtx.polygonOffsetLineState.enable(); case GL11.GL_POLYGON_OFFSET_FILL -> glCtx.polygonOffsetFillState.enable(); @@ -846,11 +863,14 @@ public class GLStateManager { case GL11.GL_TEXTURE_1D -> glCtx.textures.getTexture1DStates(glCtx.activeTextureUnit.getValue()).enable(); case GL11.GL_TEXTURE_2D -> enableTexture(glCtx); case GL12.GL_TEXTURE_3D -> glCtx.textures.getTexture3DStates(glCtx.activeTextureUnit.getValue()).enable(); + case GL13.GL_TEXTURE_CUBE_MAP -> glCtx.textures.getTextureCubeMapStates(glCtx.activeTextureUnit.getValue()).enable(); case GL11.GL_TEXTURE_GEN_S -> glCtx.textures.getTexGenSStates(glCtx.activeTextureUnit.getValue()).enable(); case GL11.GL_TEXTURE_GEN_T -> glCtx.textures.getTexGenTStates(glCtx.activeTextureUnit.getValue()).enable(); case GL11.GL_TEXTURE_GEN_R -> glCtx.textures.getTexGenRStates(glCtx.activeTextureUnit.getValue()).enable(); case GL11.GL_TEXTURE_GEN_Q -> glCtx.textures.getTexGenQStates(glCtx.activeTextureUnit.getValue()).enable(); - default -> RENDER_BACKEND.enable(cap); + default -> { + if (!setClientArrayCap(glCtx, cap, true)) RENDER_BACKEND.enable(cap); + } } } @@ -914,6 +934,8 @@ public class GLStateManager { case GL13.GL_MULTISAMPLE -> glCtx.multisampleState.disable(); case GL11.GL_NORMALIZE -> glCtx.normalizeState.disable(); case GL11.GL_POINT_SMOOTH -> glCtx.pointSmoothState.disable(); + case GL20.GL_POINT_SPRITE -> glCtx.pointSpriteState.disable(); + case GL20.GL_VERTEX_PROGRAM_TWO_SIDE -> glCtx.vertexProgramTwoSideState.disable(); case GL11.GL_POLYGON_OFFSET_POINT -> glCtx.polygonOffsetPointState.disable(); case GL11.GL_POLYGON_OFFSET_LINE -> glCtx.polygonOffsetLineState.disable(); case GL11.GL_POLYGON_OFFSET_FILL -> glCtx.polygonOffsetFillState.disable(); @@ -929,11 +951,14 @@ public class GLStateManager { case GL11.GL_TEXTURE_1D -> glCtx.textures.getTexture1DStates(glCtx.activeTextureUnit.getValue()).disable(); case GL11.GL_TEXTURE_2D -> disableTexture(glCtx); case GL12.GL_TEXTURE_3D -> glCtx.textures.getTexture3DStates(glCtx.activeTextureUnit.getValue()).disable(); + case GL13.GL_TEXTURE_CUBE_MAP -> glCtx.textures.getTextureCubeMapStates(glCtx.activeTextureUnit.getValue()).disable(); case GL11.GL_TEXTURE_GEN_S -> glCtx.textures.getTexGenSStates(glCtx.activeTextureUnit.getValue()).disable(); case GL11.GL_TEXTURE_GEN_T -> glCtx.textures.getTexGenTStates(glCtx.activeTextureUnit.getValue()).disable(); case GL11.GL_TEXTURE_GEN_R -> glCtx.textures.getTexGenRStates(glCtx.activeTextureUnit.getValue()).disable(); case GL11.GL_TEXTURE_GEN_Q -> glCtx.textures.getTexGenQStates(glCtx.activeTextureUnit.getValue()).disable(); - default -> RENDER_BACKEND.disable(cap); + default -> { + if (!setClientArrayCap(glCtx, cap, false)) RENDER_BACKEND.disable(cap); + } } } @@ -991,6 +1016,8 @@ public class GLStateManager { case GL13.GL_MULTISAMPLE -> glCtx.multisampleState.isEnabled(); case GL11.GL_NORMALIZE -> glCtx.normalizeState.isEnabled(); case GL11.GL_POINT_SMOOTH -> glCtx.pointSmoothState.isEnabled(); + case GL20.GL_POINT_SPRITE -> glCtx.pointSpriteState.isEnabled(); + case GL20.GL_VERTEX_PROGRAM_TWO_SIDE -> glCtx.vertexProgramTwoSideState.isEnabled(); case GL11.GL_POLYGON_OFFSET_POINT -> glCtx.polygonOffsetPointState.isEnabled(); case GL11.GL_POLYGON_OFFSET_LINE -> glCtx.polygonOffsetLineState.isEnabled(); case GL11.GL_POLYGON_OFFSET_FILL -> glCtx.polygonOffsetFillState.isEnabled(); @@ -1006,16 +1033,23 @@ public class GLStateManager { case GL11.GL_TEXTURE_1D -> glCtx.textures.getTexture1DStates(glCtx.activeTextureUnit.getValue()).isEnabled(); case GL11.GL_TEXTURE_2D -> glCtx.textures.getTextureUnitStates(glCtx.activeTextureUnit.getValue()).isEnabled(); case GL12.GL_TEXTURE_3D -> glCtx.textures.getTexture3DStates(glCtx.activeTextureUnit.getValue()).isEnabled(); + case GL13.GL_TEXTURE_CUBE_MAP -> glCtx.textures.getTextureCubeMapStates(glCtx.activeTextureUnit.getValue()).isEnabled(); case GL11.GL_TEXTURE_GEN_S -> glCtx.textures.getTexGenSStates(glCtx.activeTextureUnit.getValue()).isEnabled(); case GL11.GL_TEXTURE_GEN_T -> glCtx.textures.getTexGenTStates(glCtx.activeTextureUnit.getValue()).isEnabled(); case GL11.GL_TEXTURE_GEN_R -> glCtx.textures.getTexGenRStates(glCtx.activeTextureUnit.getValue()).isEnabled(); case GL11.GL_TEXTURE_GEN_Q -> glCtx.textures.getTexGenQStates(glCtx.activeTextureUnit.getValue()).isEnabled(); - default -> RENDER_BACKEND.getBoolean(cap); + default -> { + final int conv = clientStateToConventional(glCtx, cap); + yield conv >= 0 ? glCtx.vaos.isConventionalEnabled(conv) : LegacyQueries.desc(cap) == 0 && RENDER_BACKEND.getBoolean(cap); + } }; } public static boolean glGetBoolean(int pname) { - final GLContextState glCtx = ctx(); + return glGetBoolean(ctx(), pname); + } + + static boolean glGetBoolean(GLContextState glCtx, int pname) { if (!isCachingEnabled()) { return RENDER_BACKEND.getBoolean(pname); } @@ -1069,6 +1103,8 @@ public class GLStateManager { case GL13.GL_MULTISAMPLE -> glCtx.multisampleState.isEnabled(); case GL11.GL_NORMALIZE -> glCtx.normalizeState.isEnabled(); case GL11.GL_POINT_SMOOTH -> glCtx.pointSmoothState.isEnabled(); + case GL20.GL_POINT_SPRITE -> glCtx.pointSpriteState.isEnabled(); + case GL20.GL_VERTEX_PROGRAM_TWO_SIDE -> glCtx.vertexProgramTwoSideState.isEnabled(); case GL11.GL_POLYGON_OFFSET_POINT -> glCtx.polygonOffsetPointState.isEnabled(); case GL11.GL_POLYGON_OFFSET_LINE -> glCtx.polygonOffsetLineState.isEnabled(); case GL11.GL_POLYGON_OFFSET_FILL -> glCtx.polygonOffsetFillState.isEnabled(); @@ -1084,13 +1120,20 @@ public class GLStateManager { case GL11.GL_TEXTURE_1D -> glCtx.textures.getTexture1DStates(glCtx.activeTextureUnit.getValue()).isEnabled(); case GL11.GL_TEXTURE_2D -> glCtx.textures.getTextureUnitStates(glCtx.activeTextureUnit.getValue()).isEnabled(); case GL12.GL_TEXTURE_3D -> glCtx.textures.getTexture3DStates(glCtx.activeTextureUnit.getValue()).isEnabled(); + case GL13.GL_TEXTURE_CUBE_MAP -> glCtx.textures.getTextureCubeMapStates(glCtx.activeTextureUnit.getValue()).isEnabled(); case GL11.GL_TEXTURE_GEN_S -> glCtx.textures.getTexGenSStates(glCtx.activeTextureUnit.getValue()).isEnabled(); case GL11.GL_TEXTURE_GEN_T -> glCtx.textures.getTexGenTStates(glCtx.activeTextureUnit.getValue()).isEnabled(); case GL11.GL_TEXTURE_GEN_R -> glCtx.textures.getTexGenRStates(glCtx.activeTextureUnit.getValue()).isEnabled(); case GL11.GL_TEXTURE_GEN_Q -> glCtx.textures.getTexGenQStates(glCtx.activeTextureUnit.getValue()).isEnabled(); case GL11.GL_LIGHT_MODEL_LOCAL_VIEWER -> glCtx.lightModel.localViewer != 0.0f; case GL11.GL_LIGHT_MODEL_TWO_SIDE -> glCtx.lightModel.twoSide != 0.0f; - default -> RENDER_BACKEND.getBoolean(pname); + case GL11.GL_EDGE_FLAG -> glCtx.edgeFlagState.isEnabled(); + default -> { + final int conv = clientStateToConventional(glCtx, pname); + if (conv >= 0) yield glCtx.vaos.isConventionalEnabled(conv); + final int desc = LegacyQueries.desc(pname); + yield desc != 0 ? LegacyQueries.getBoolean(glCtx, pname, desc) : RENDER_BACKEND.getBoolean(pname); + } }; } @@ -1111,8 +1154,8 @@ public class GLStateManager { } default -> { if (!HAS_MULTIPLE_SET.contains(pname)) { - params.put(0, (byte) (glGetBoolean(pname) ? GL11.GL_TRUE : GL11.GL_FALSE)); - } else { + params.put(params.position(), (byte) (glGetBoolean(glCtx, pname) ? GL11.GL_TRUE : GL11.GL_FALSE)); + } else if (!LegacyQueries.getBooleanv(glCtx, pname, params)) { RENDER_BACKEND.getBoolean(pname, params); } } @@ -1126,7 +1169,10 @@ public class GLStateManager { } public static int glGetInteger(int pname) { - final GLContextState glCtx = ctx(); + return glGetInteger(ctx(), pname); + } + + static int glGetInteger(GLContextState glCtx, int pname) { if (!isCachingEnabled()) { return RENDER_BACKEND.getInteger(pname); } @@ -1141,6 +1187,7 @@ public class GLStateManager { // GL_TEXTURE_2D makes no sense here, but some mod still queries it... case GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_BINDING_2D -> getBoundTextureForServerState(); case GL13.GL_ACTIVE_TEXTURE -> GL13.GL_TEXTURE0 + getActiveTextureUnitForServerState(); + case GL13.GL_CLIENT_ACTIVE_TEXTURE -> GL13.GL_TEXTURE0 + glCtx.clientActiveTextureUnit; case GL11.GL_COLOR_MATERIAL_FACE -> glCtx.colorMaterialFace.getValue(); case GL11.GL_COLOR_MATERIAL_PARAMETER -> glCtx.colorMaterialParameter.getValue(); case GL11.GL_MODELVIEW_STACK_DEPTH -> getMatrixStackDepth(glCtx.modelViewMatrix); @@ -1206,7 +1253,17 @@ public class GLStateManager { case GL11.GL_MAX_LIGHTS -> glCtx.lightStates.length; case GL11.GL_STENCIL_BITS -> drawFramebufferBits(glCtx, true); case GL11.GL_DEPTH_BITS -> drawFramebufferBits(glCtx, false); - default -> RENDER_BACKEND.getInteger(pname); + case GL20.GL_MAX_TEXTURE_COORDS -> MAX_TEXTURE_COORDS; + case GL20.GL_POINT_SPRITE_COORD_ORIGIN -> glCtx.pointState.getSpriteCoordOrigin(); + case GL11.GL_RENDER_MODE -> FeedbackManager.getRenderMode(); + default -> { + final int shift = hintShift(pname); + if (shift >= 0) yield hintMode(glCtx.hintState.getValue(), shift); + final int key = clientArrayField(glCtx, pname); + if (key >= 0 && (key & ~0xFF) != FIELD_POINTER) yield clientArrayParam(glCtx, key); + final int desc = LegacyQueries.desc(pname); + yield desc != 0 ? LegacyQueries.getInteger(glCtx, pname, desc) : RENDER_BACKEND.getInteger(pname); + } }; }; } @@ -1239,9 +1296,10 @@ public class GLStateManager { params.put(pos + 1, polygon.getBackMode()); } default -> { + final GLContextState glCtx = ctx(); if (!HAS_MULTIPLE_SET.contains(pname)) { - params.put(0, glGetInteger(pname)); - } else { + params.put(params.position(), glGetInteger(glCtx, pname)); + } else if (!LegacyQueries.getIntegerv(glCtx, pname, params)) { RENDER_BACKEND.getInteger(pname, params); } } @@ -1260,12 +1318,12 @@ public class GLStateManager { } switch (pname) { - case GL11.GL_AMBIENT -> state.ambient.get(0, params); - case GL11.GL_DIFFUSE -> state.diffuse.get(0, params); - case GL11.GL_SPECULAR -> state.specular.get(0, params); - case GL11.GL_EMISSION -> state.emission.get(0, params); + case GL11.GL_AMBIENT -> state.ambient.get(params.position(), params); + case GL11.GL_DIFFUSE -> state.diffuse.get(params.position(), params); + case GL11.GL_SPECULAR -> state.specular.get(params.position(), params); + case GL11.GL_EMISSION -> state.emission.get(params.position(), params); case GL11.GL_SHININESS -> params.put(params.position(), state.shininess); - case GL11.GL_COLOR_INDEXES -> state.colorIndexes.get(0, params); + case GL11.GL_COLOR_INDEXES -> state.colorIndexes.get(params.position(), params); default -> { } } @@ -1289,9 +1347,9 @@ public class GLStateManager { final float value = values.get(i); final int converted; if (color) { - converted = (int) (2147483647.0 * value); + converted = LegacyQueries.floatToInt(value); } else if (material) { - converted = value < 0 ? -Math.round(-value) : Math.round(value); + converted = LegacyQueries.lround(value); } else { converted = (int) value; } @@ -1316,11 +1374,11 @@ public class GLStateManager { public static void glGetLight(int light, int pname, FloatBuffer params) { final LightStateStack state = ctx().lightDataStates[light - GL11.GL_LIGHT0]; switch (pname) { - case GL11.GL_AMBIENT -> state.ambient.get(0, params); - case GL11.GL_DIFFUSE -> state.diffuse.get(0, params); - case GL11.GL_SPECULAR -> state.specular.get(0, params); - case GL11.GL_POSITION -> state.position.get(0, params); - case GL11.GL_SPOT_DIRECTION -> state.spotDirection.get(0, params); + case GL11.GL_AMBIENT -> state.ambient.get(params.position(), params); + case GL11.GL_DIFFUSE -> state.diffuse.get(params.position(), params); + case GL11.GL_SPECULAR -> state.specular.get(params.position(), params); + case GL11.GL_POSITION -> state.position.get(params.position(), params); + case GL11.GL_SPOT_DIRECTION -> state.spotDirection.get(params.position(), params); case GL11.GL_SPOT_EXPONENT -> params.put(params.position(), state.spotExponent); case GL11.GL_SPOT_CUTOFF -> params.put(params.position(), state.spotCutoff); case GL11.GL_CONSTANT_ATTENUATION -> params.put(params.position(), state.constantAttenuation); @@ -1339,9 +1397,9 @@ public class GLStateManager { } switch (pname) { - case GL11.GL_MODELVIEW_MATRIX -> glCtx.modelViewMatrix.get(0, params); - case GL11.GL_PROJECTION_MATRIX -> glCtx.projectionMatrix.get(0, params); - case GL11.GL_TEXTURE_MATRIX -> glCtx.textures.getTextureUnitMatrix(getActiveTextureUnit()).get(0, params); + case GL11.GL_MODELVIEW_MATRIX -> glCtx.modelViewMatrix.get(params.position(), params); + case GL11.GL_PROJECTION_MATRIX -> glCtx.projectionMatrix.get(params.position(), params); + case GL11.GL_TEXTURE_MATRIX -> glCtx.textures.getTextureUnitMatrix(getActiveTextureUnit()).get(params.position(), params); case GL11.GL_COLOR_CLEAR_VALUE -> glCtx.clearColor.get(params); case GL11.GL_CURRENT_COLOR -> glCtx.color.get(params); case GL14.GL_CURRENT_SECONDARY_COLOR -> glCtx.secondaryColor.get(params); @@ -1393,10 +1451,16 @@ public class GLStateManager { params.put(pos + 2, fogBuf.get(2)); params.put(pos + 3, fogBuf.get(3)); } - case GL11.GL_LIGHT_MODEL_AMBIENT -> glCtx.lightModel.ambient.get(0, params); + case GL11.GL_LIGHT_MODEL_AMBIENT -> glCtx.lightModel.ambient.get(params.position(), params); + case GL14.GL_POINT_DISTANCE_ATTENUATION -> { + final int pos = params.position(); + params.put(pos, glCtx.pointState.getAttenuationConstant()); + params.put(pos + 1, glCtx.pointState.getAttenuationLinear()); + params.put(pos + 2, glCtx.pointState.getAttenuationQuadratic()); + } default -> { if (!HAS_MULTIPLE_SET.contains(pname)) { - params.put(0, glGetFloat(pname)); + params.put(params.position(), glGetFloat(glCtx, pname)); } else { RENDER_BACKEND.getFloat(pname, params); } @@ -1405,7 +1469,10 @@ public class GLStateManager { } public static float glGetFloat(int pname) { - final GLContextState glCtx = ctx(); + return glGetFloat(ctx(), pname); + } + + static float glGetFloat(GLContextState glCtx, int pname) { return switch (pname) { case GL11.GL_ALPHA_TEST_REF -> glCtx.alphaState.getReference(); case GL11.GL_FOG_DENSITY -> glCtx.fogState.getDensity(); @@ -1414,7 +1481,13 @@ public class GLStateManager { case GL11.GL_DEPTH_CLEAR_VALUE -> (float) glCtx.depthState.getClearValue(); case GL11.GL_LINE_WIDTH -> glCtx.lineState.getWidth(); case GL11.GL_POINT_SIZE -> glCtx.pointState.getSize(); - default -> RENDER_BACKEND.getFloat(pname); + case GL14.GL_POINT_SIZE_MIN -> glCtx.pointState.getMinSize(); + case GL14.GL_POINT_SIZE_MAX -> Float.isNaN(glCtx.pointState.getMaxSize()) ? maxPointSize(glCtx) : glCtx.pointState.getMaxSize(); + case GL14.GL_POINT_FADE_THRESHOLD_SIZE -> glCtx.pointState.getFadeThreshold(); + default -> { + final int desc = isCachingEnabled() ? LegacyQueries.desc(pname) : 0; + yield desc != 0 ? LegacyQueries.getFloat(glCtx, pname, desc) : RENDER_BACKEND.getFloat(pname); + } }; } @@ -1457,7 +1530,7 @@ public class GLStateManager { case GL11.GL_MODELVIEW_MATRIX, GL11.GL_PROJECTION_MATRIX, GL11.GL_TEXTURE_MATRIX -> widenFromFloat(pname, params, 16); case GL11.GL_COLOR_CLEAR_VALUE, GL11.GL_CURRENT_COLOR, GL11.GL_CURRENT_TEXTURE_COORDS, GL11.GL_FOG_COLOR, GL11.GL_LIGHT_MODEL_AMBIENT, GL14.GL_BLEND_COLOR, GL14.GL_CURRENT_SECONDARY_COLOR -> widenFromFloat(pname, params, 4); - case GL11.GL_CURRENT_NORMAL -> widenFromFloat(pname, params, 3); + case GL11.GL_CURRENT_NORMAL, GL14.GL_POINT_DISTANCE_ATTENUATION -> widenFromFloat(pname, params, 3); default -> { if (!HAS_MULTIPLE_SET.contains(pname)) { params.put(pos, glGetDouble(pname)); @@ -1885,7 +1958,12 @@ public class GLStateManager { } public static void glEdgeFlag(boolean flag) { - // No-op: edge flags are removed in GL 3.3 core profile and have no equivalent + final RecordMode mode = DisplayListManager.getRecordMode(); + if (mode != RecordMode.NONE) { + DisplayListManager.recordStateCommand(() -> glEdgeFlag(flag)); + if (mode == RecordMode.COMPILE) return; + } + ctx().edgeFlagState.setEnabled(flag); } public static void glColor4f(float red, float green, float blue, float alpha) { @@ -2214,7 +2292,7 @@ public class GLStateManager { } } final int newTexture = texture - GL13.GL_TEXTURE0; - if (!isValidTextureUnit(newTexture, texture)) { + if (!isValidTextureUnit(newTexture, texture, "glActiveTexture")) { return; } final boolean caching = isCachingEnabled(); @@ -2224,52 +2302,38 @@ public class GLStateManager { } } - private static boolean isValidTextureUnit(int unit, int rawEnum) { - if (unit >= 0 && unit < MAX_TEXTURE_UNITS) { + private static boolean isValidTextureUnit(int unit, int rawEnum, String function) { + return isValidTextureUnit(unit, MAX_TEXTURE_UNITS, rawEnum, function); + } + + private static boolean isValidTextureUnit(int unit, int limit, int rawEnum, String function) { + if (unit >= 0 && unit < limit) { return true; } - if (!warnedInvalidTextureUnit) { - warnedInvalidTextureUnit = true; - LOGGER.warn("glActiveTexture({}) is not a valid texture unit enum (expected GL_TEXTURE0..GL_TEXTURE{}); ignoring", rawEnum, MAX_TEXTURE_UNITS - 1, new Throwable("call site")); + if (WARN_ONCE.add(function)) { + LOGGER.warn("{}({}) is not a valid texture unit enum (expected GL_TEXTURE0..GL_TEXTURE{}); ignoring", function, rawEnum, limit - 1, new Throwable("call site")); } return false; } - private static boolean warnedInvalidTextureUnit = false; - public static void glActiveTextureARB(int texture) { - final RecordMode mode = DisplayListManager.getRecordMode(); - if (mode != RecordMode.NONE) { - DisplayListManager.recordActiveTexture(texture); - if (mode == RecordMode.COMPILE) { - return; - } - } - final int newTexture = texture - GL13.GL_TEXTURE0; - if (!isValidTextureUnit(newTexture, texture)) { - return; - } - final boolean caching = isCachingEnabled(); - if (!caching || getActiveTextureUnit() != newTexture) { - mod(ctx().activeTextureUnit).setValue(newTexture); - RENDER_BACKEND.activeTexture(texture); - } + glActiveTexture(texture); } public static void glClientActiveTexture(int texture) { - ctx().clientActiveTextureUnit = texture - GL13.GL_TEXTURE0; + final int unit = texture - GL13.GL_TEXTURE0; + if (!isValidTextureUnit(unit, MAX_TEXTURE_COORDS, texture, "glClientActiveTexture")) { + return; + } + ctx().clientActiveTextureUnit = unit; } public static int getClientActiveTextureUnit() { return ctx().clientActiveTextureUnit; } - private static int texCoordAttributeLocation() { - return switch (ctx().clientActiveTextureUnit) { - case 0 -> Usage.PRIMARY_UV.getAttributeLocation(); - case 1 -> Usage.SECONDARY_UV.getAttributeLocation(); - default -> -1; - }; + private static int texCoordConventional(GLContextState glCtx) { + return VAOManager.CONV_TEX0 + glCtx.clientActiveTextureUnit; } private static int getBoundTexture() { @@ -2295,6 +2359,7 @@ public class GLStateManager { final boolean locked = acquireDrawLock(); try { deferredDeleteTextures.remove(texture); + if (target != GL11.GL_TEXTURE_2D) unshrinkableTextures.add(texture); } finally { if (locked) releaseDrawLock(); } @@ -2559,17 +2624,21 @@ public class GLStateManager { if (id == 0) return; final int savedUnit = glCtx.activeTextureUnit.getValue(); - final int savedBinding = glCtx.textures.getTextureUnitBindings(savedUnit).getBinding(); + final TextureBindingStack savedUnitBindings = glCtx.textures.getTextureUnitBindings(savedUnit); + final int savedBinding = savedUnitBindings.getBinding(); + final int savedTarget = savedUnitBindings.getTarget(); boolean changedUnit = false; // Unbind from all units that have this texture for (int i = 0; i <= glCtx.maxBoundTextureUnit; i++) { - if (glCtx.textures.getTextureUnitBindings(i).getBinding() == id) { + final TextureBindingStack unitBindings = glCtx.textures.getTextureUnitBindings(i); + if (unitBindings.getBinding() == id) { if (i != savedUnit) { RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + i); changedUnit = true; } - RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, 0); + final int unitTarget = unitBindings.getTarget(); + RENDER_BACKEND.bindTexture(unitTarget == 0 ? GL11.GL_TEXTURE_2D : unitTarget, 0); } } @@ -2577,6 +2646,17 @@ public class GLStateManager { if (changedUnit) { RENDER_BACKEND.activeTexture(GL13.GL_TEXTURE0 + savedUnit); } + + final boolean shrinkable; + final boolean locked = acquireDrawLock(); + try { + shrinkable = !unshrinkableTextures.contains(id); + deferredDeleteTextures.add(id); + } finally { + if (locked) releaseDrawLock(); + } + if (!shrinkable) return; + RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, id); RENDER_BACKEND.texParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 0); suspendPixelUnpackBuffer(); @@ -2584,14 +2664,8 @@ public class GLStateManager { restorePixelUnpackBuffer(); // Restore previous binding on active unit - RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, savedBinding == id ? 0 : savedBinding); - - final boolean locked = acquireDrawLock(); - try { - deferredDeleteTextures.add(id); - } finally { - if (locked) releaseDrawLock(); - } + final boolean savedIs2D = savedTarget == GL11.GL_TEXTURE_2D || savedTarget == 0; + RENDER_BACKEND.bindTexture(GL11.GL_TEXTURE_2D, savedBinding == id || !savedIs2D ? 0 : savedBinding); } /** Flush deferred deletes so the driver can recycle names. */ @@ -2603,6 +2677,7 @@ public class GLStateManager { while (it.hasNext()) { final int id = it.nextInt(); RENDER_BACKEND.deleteTextures(id); + unshrinkableTextures.remove(id); clearTextureBindings(primary, id); for (int i = 0; i < drawables.length; i++) { clearTextureBindings(drawables[i], id); @@ -3040,11 +3115,104 @@ public class GLStateManager { } public static void glPointParameterf(int pname, float param) { - RENDER_BACKEND.pointParameterf(pname, param); + pointParameter(pname, param, 0.0f, 0.0f); } public static void glPointParameteri(int pname, int param) { - RENDER_BACKEND.pointParameteri(pname, param); + pointParameter(pname, param, 0.0f, 0.0f); + } + + public static void glPointParameter(int pname, FloatBuffer params) { + final int pos = params.position(); + final boolean attenuation = pname == GL14.GL_POINT_DISTANCE_ATTENUATION; + pointParameter(pname, params.get(pos), attenuation ? params.get(pos + 1) : 0.0f, attenuation ? params.get(pos + 2) : 0.0f); + } + + public static void glPointParameter(int pname, IntBuffer params) { + final int pos = params.position(); + final boolean attenuation = pname == GL14.GL_POINT_DISTANCE_ATTENUATION; + pointParameter(pname, params.get(pos), attenuation ? params.get(pos + 1) : 0.0f, attenuation ? params.get(pos + 2) : 0.0f); + } + + private static void pointParameter(int pname, float v0, float v1, float v2) { + final GLContextState glCtx = ctx(); + final PointStateStack point = glCtx.pointState; + switch (pname) { + case GL14.GL_POINT_SIZE_MIN -> { + if (!(v0 < 0.0f) && point.getMinSize() != v0) mod(point).setMinSize(v0); + } + case GL14.GL_POINT_SIZE_MAX -> { + if (!(v0 < 0.0f) && point.getMaxSize() != v0) mod(point).setMaxSize(v0); + } + case GL14.GL_POINT_FADE_THRESHOLD_SIZE -> { + if (!(v0 < 0.0f) && point.getFadeThreshold() != v0) { + mod(point).setFadeThreshold(v0); + RENDER_BACKEND.pointParameterf(pname, v0); + } + } + case GL14.GL_POINT_DISTANCE_ATTENUATION -> { + if (point.getAttenuationConstant() == v0 && point.getAttenuationLinear() == v1 && point.getAttenuationQuadratic() == v2) return; + mod(point); + point.setAttenuationConstant(v0); + point.setAttenuationLinear(v1); + point.setAttenuationQuadratic(v2); + } + case GL20.GL_POINT_SPRITE_COORD_ORIGIN -> { + final int origin = (int) v0; + if ((origin == GL20.GL_LOWER_LEFT || origin == GL20.GL_UPPER_LEFT) && point.getSpriteCoordOrigin() != origin) { + mod(point).setSpriteCoordOrigin(origin); + RENDER_BACKEND.pointParameteri(pname, origin); + } + } + default -> RENDER_BACKEND.pointParameterf(pname, v0); + } + } + + private static void applyPointState(GLContextState glCtx) { + final PointStateStack point = glCtx.pointState; + RENDER_BACKEND.pointSize(point.getSize()); + RENDER_BACKEND.pointParameterf(GL14.GL_POINT_FADE_THRESHOLD_SIZE, point.getFadeThreshold()); + RENDER_BACKEND.pointParameteri(GL20.GL_POINT_SPRITE_COORD_ORIGIN, point.getSpriteCoordOrigin()); + } + + private static boolean isLegacyHint(int target) { + return switch (target) { + case GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_POINT_SMOOTH_HINT, GL11.GL_FOG_HINT, GL14.GL_GENERATE_MIPMAP_HINT -> true; + default -> false; + }; + } + + private static int hintShift(int target) { + return switch (target) { + case GL11.GL_PERSPECTIVE_CORRECTION_HINT -> 0; + case GL11.GL_POINT_SMOOTH_HINT -> 2; + case GL11.GL_LINE_SMOOTH_HINT -> 4; + case GL11.GL_POLYGON_SMOOTH_HINT -> 6; + case GL11.GL_FOG_HINT -> 8; + case GL13.GL_TEXTURE_COMPRESSION_HINT -> 10; + case GL14.GL_GENERATE_MIPMAP_HINT -> 12; + case GL20.GL_FRAGMENT_SHADER_DERIVATIVE_HINT -> 14; + default -> -1; + }; + } + + private static int hintMode(int packed, int shift) { + return switch (packed >>> shift & 3) { + case 1 -> GL11.GL_FASTEST; + case 2 -> GL11.GL_NICEST; + default -> GL11.GL_DONT_CARE; + }; + } + + private static final int[] DRIVER_HINTS = { + GL11.GL_LINE_SMOOTH_HINT, GL11.GL_POLYGON_SMOOTH_HINT, GL13.GL_TEXTURE_COMPRESSION_HINT, GL20.GL_FRAGMENT_SHADER_DERIVATIVE_HINT + }; + + private static void applyHintState(GLContextState glCtx) { + final int packed = glCtx.hintState.getValue(); + for (final int target : DRIVER_HINTS) { + RENDER_BACKEND.hint(target, hintMode(packed, hintShift(target))); + } } private static void prepareClientArrays(GLContextState glCtx) { @@ -3101,19 +3269,167 @@ public class GLStateManager { } } - private static void ffpClientArrayPointer(int index, int size, int type, boolean normalized, int stride, long offset) { - final GLContextState glCtx = ctx(); - if (glCtx.vaos.isGenericPointerEnabled(index)) return; - glCtx.vaos.setAttribute(index, size, type, normalized, stride, offset, glCtx.boundVBO); - glCtx.vaos.markConventional(index); - RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, offset); + private static boolean isLegalClientArray(int conv, int size, int type, int stride) { + return isLegalPointer(VAOManager.isLegalConventional(conv, size, type), "client-array", "client array", conv, size, type, stride); } - private static void ffpClientArrayPointer(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) { - final VAOManager vaos = ctx().vaos; - if (vaos.isGenericPointerEnabled(index)) return; - vaos.setAttribute(index, size, type, normalized, stride, pointer); - vaos.markConventional(index); + private static boolean isLegalVertexAttrib(int index, int size, int type, int stride, boolean integer) { + return isLegalPointer(VAOManager.isLegalGeneric(index, size, type, integer), "vertex-attrib", "vertex attribute", index, size, type, stride); + } + + // The WARN_ONCE check comes first so a caller repeating the mistake every frame does not capture a stack each time. + private static boolean isLegalPointer(boolean legal, String key, String what, int slot, int size, int type, int stride) { + if (stride < 0 && WARN_ONCE.add(key + "-stride")) { + LOGGER.warn("Negative {} stride {}", what, stride, new Throwable("call site")); + } + if (legal) return true; + if (WARN_ONCE.add(key + "-" + slot)) { + LOGGER.warn("Ignoring {} {} with illegal size {} / type 0x{}", what, slot, size, Integer.toHexString(type), new Throwable("call site")); + } + return false; + } + + private static boolean isValidVertexAttribIndex(int index, String function) { + if (index >= 0 && index < VAOManager.MAX_ATTRIBS) return true; + if (WARN_ONCE.add(function)) { + LOGGER.warn("{}({}) is outside the {} supported vertex attributes; ignoring", function, index, VAOManager.MAX_ATTRIBS, new Throwable("call site")); + } + return false; + } + + private static void ffpClientArrayPointer(int conv, int size, int type, boolean normalized, int stride, long offset) { + if (!isLegalClientArray(conv, size, type, stride)) return; + final GLContextState glCtx = ctx(); + if (glCtx.vaos.setConventional(conv, size, type, normalized, stride, offset, glCtx.boundVBO)) { + RENDER_BACKEND.vertexAttribPointer(VAOManager.conventionalLocation(conv), size, type, normalized, stride, offset); + } + } + + private static void ffpClientArrayPointer(int conv, int size, int type, boolean normalized, int stride, ByteBuffer pointer) { + if (!isLegalClientArray(conv, size, type, stride)) return; + ctx().vaos.setConventional(conv, size, type, normalized, stride, pointer); + } + + private static final int FIELD_SIZE = 0, FIELD_TYPE = 1 << 8, FIELD_STRIDE = 2 << 8, FIELD_BINDING = 3 << 8, FIELD_POINTER = 4 << 8; + + private static int clientArrayField(GLContextState glCtx, int pname) { + return switch (pname) { + case GL11.GL_VERTEX_ARRAY_SIZE -> VAOManager.CONV_POSITION | FIELD_SIZE; + case GL11.GL_VERTEX_ARRAY_TYPE -> VAOManager.CONV_POSITION | FIELD_TYPE; + case GL11.GL_VERTEX_ARRAY_STRIDE -> VAOManager.CONV_POSITION | FIELD_STRIDE; + case GL15.GL_VERTEX_ARRAY_BUFFER_BINDING -> VAOManager.CONV_POSITION | FIELD_BINDING; + case GL11.GL_VERTEX_ARRAY_POINTER -> VAOManager.CONV_POSITION | FIELD_POINTER; + case GL11.GL_NORMAL_ARRAY_TYPE -> VAOManager.CONV_NORMAL | FIELD_TYPE; + case GL11.GL_NORMAL_ARRAY_STRIDE -> VAOManager.CONV_NORMAL | FIELD_STRIDE; + case GL15.GL_NORMAL_ARRAY_BUFFER_BINDING -> VAOManager.CONV_NORMAL | FIELD_BINDING; + case GL11.GL_NORMAL_ARRAY_POINTER -> VAOManager.CONV_NORMAL | FIELD_POINTER; + case GL11.GL_COLOR_ARRAY_SIZE -> VAOManager.CONV_COLOR | FIELD_SIZE; + case GL11.GL_COLOR_ARRAY_TYPE -> VAOManager.CONV_COLOR | FIELD_TYPE; + case GL11.GL_COLOR_ARRAY_STRIDE -> VAOManager.CONV_COLOR | FIELD_STRIDE; + case GL15.GL_COLOR_ARRAY_BUFFER_BINDING -> VAOManager.CONV_COLOR | FIELD_BINDING; + case GL11.GL_COLOR_ARRAY_POINTER -> VAOManager.CONV_COLOR | FIELD_POINTER; + case GL11.GL_INDEX_ARRAY_TYPE -> VAOManager.CONV_INDEX | FIELD_TYPE; + case GL11.GL_INDEX_ARRAY_STRIDE -> VAOManager.CONV_INDEX | FIELD_STRIDE; + case GL15.GL_INDEX_ARRAY_BUFFER_BINDING -> VAOManager.CONV_INDEX | FIELD_BINDING; + case GL11.GL_INDEX_ARRAY_POINTER -> VAOManager.CONV_INDEX | FIELD_POINTER; + case GL11.GL_EDGE_FLAG_ARRAY_STRIDE -> VAOManager.CONV_EDGE_FLAG | FIELD_STRIDE; + case GL15.GL_EDGE_FLAG_ARRAY_BUFFER_BINDING -> VAOManager.CONV_EDGE_FLAG | FIELD_BINDING; + case GL11.GL_EDGE_FLAG_ARRAY_POINTER -> VAOManager.CONV_EDGE_FLAG | FIELD_POINTER; + case GL14.GL_SECONDARY_COLOR_ARRAY_SIZE -> VAOManager.CONV_SECONDARY_COLOR | FIELD_SIZE; + case GL14.GL_SECONDARY_COLOR_ARRAY_TYPE -> VAOManager.CONV_SECONDARY_COLOR | FIELD_TYPE; + case GL14.GL_SECONDARY_COLOR_ARRAY_STRIDE -> VAOManager.CONV_SECONDARY_COLOR | FIELD_STRIDE; + case GL15.GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING -> VAOManager.CONV_SECONDARY_COLOR | FIELD_BINDING; + case GL14.GL_SECONDARY_COLOR_ARRAY_POINTER -> VAOManager.CONV_SECONDARY_COLOR | FIELD_POINTER; + case GL14.GL_FOG_COORDINATE_ARRAY_TYPE -> VAOManager.CONV_FOG_COORD | FIELD_TYPE; + case GL14.GL_FOG_COORDINATE_ARRAY_STRIDE -> VAOManager.CONV_FOG_COORD | FIELD_STRIDE; + case GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING -> VAOManager.CONV_FOG_COORD | FIELD_BINDING; + case GL14.GL_FOG_COORDINATE_ARRAY_POINTER -> VAOManager.CONV_FOG_COORD | FIELD_POINTER; + case GL11.GL_TEXTURE_COORD_ARRAY_SIZE -> texCoordConventional(glCtx) | FIELD_SIZE; + case GL11.GL_TEXTURE_COORD_ARRAY_TYPE -> texCoordConventional(glCtx) | FIELD_TYPE; + case GL11.GL_TEXTURE_COORD_ARRAY_STRIDE -> texCoordConventional(glCtx) | FIELD_STRIDE; + case GL15.GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING -> texCoordConventional(glCtx) | FIELD_BINDING; + case GL11.GL_TEXTURE_COORD_ARRAY_POINTER -> texCoordConventional(glCtx) | FIELD_POINTER; + default -> -1; + }; + } + + private static int clientArrayParam(GLContextState glCtx, int key) { + final int conv = key & 0xFF; + final VAOManager.Attrib a = glCtx.vaos.conventional(conv); + final boolean specified = a != null && a.size != 0; + return switch (key & ~0xFF) { + case FIELD_SIZE -> specified ? a.size : conv == VAOManager.CONV_SECONDARY_COLOR ? 3 : 4; + case FIELD_TYPE -> specified ? a.type : GL11.GL_FLOAT; + case FIELD_STRIDE -> specified ? a.stride : 0; + default -> specified ? a.vboId : 0; + }; + } + + public static ByteBuffer glGetPointer(int pname, long result_size) { + final GLContextState glCtx = ctx(); + final int key = clientArrayField(glCtx, pname); + if ((key & ~0xFF) != FIELD_POINTER) return null; + final VAOManager.Attrib a = glCtx.vaos.conventional(key & 0xFF); + if (a == null) return null; + final long address = a.clientPointer != null ? MemoryUtilities.memAddress(a.clientPointer) : a.offset; + return address == 0 ? null : MemoryUtilities.memByteBuffer(address, (int) result_size); + } + + public static void glEdgeFlagPointer(int stride, ByteBuffer pointer) { + ffpClientArrayPointer(VAOManager.CONV_EDGE_FLAG, 1, GL11.GL_UNSIGNED_BYTE, false, stride, pointer); + } + + public static void glEdgeFlagPointer(int stride, long pointer_buffer_offset) { + ffpClientArrayPointer(VAOManager.CONV_EDGE_FLAG, 1, GL11.GL_UNSIGNED_BYTE, false, stride, pointer_buffer_offset); + } + + public static void glFogCoordPointer(int stride, FloatBuffer data) { + ffpClientArrayPointer(VAOManager.CONV_FOG_COORD, 1, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(data)); + } + + public static void glFogCoordPointer(int stride, DoubleBuffer data) { + ffpClientArrayPointer(VAOManager.CONV_FOG_COORD, 1, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(data)); + } + + public static void glFogCoordPointer(int type, int stride, long data_buffer_offset) { + ffpClientArrayPointer(VAOManager.CONV_FOG_COORD, 1, type, false, stride, data_buffer_offset); + } + + public static void glSecondaryColorPointer(int size, int stride, FloatBuffer data) { + ffpClientArrayPointer(VAOManager.CONV_SECONDARY_COLOR, size, GL11.GL_FLOAT, true, stride, MemoryUtilities.memByteBuffer(data)); + } + + public static void glSecondaryColorPointer(int size, int stride, DoubleBuffer data) { + ffpClientArrayPointer(VAOManager.CONV_SECONDARY_COLOR, size, GL11.GL_DOUBLE, true, stride, MemoryUtilities.memByteBuffer(data)); + } + + public static void glSecondaryColorPointer(int size, boolean unsigned, int stride, ByteBuffer data) { + final int type = unsigned ? GL11.GL_UNSIGNED_BYTE : GL11.GL_BYTE; + ffpClientArrayPointer(VAOManager.CONV_SECONDARY_COLOR, size, type, true, stride, data); + } + + public static void glSecondaryColorPointer(int size, int type, int stride, long data_buffer_offset) { + ffpClientArrayPointer(VAOManager.CONV_SECONDARY_COLOR, size, type, true, stride, data_buffer_offset); + } + + private static void pointAttrib(GLContextState glCtx, int index) { + final VAOManager vaos = glCtx.vaos; + final VAOManager.Attrib a = vaos.attrib(index); + if (a == null) return; + RENDER_BACKEND.vertexAttribDivisor(index, a.divisor); + if (a.size == 0 || a.vboId == 0 || !vaos.isBufferLive(a)) return; + if (glCtx.boundVBO != a.vboId) glBindBuffer(GL15.GL_ARRAY_BUFFER, a.vboId); + if (a.integer) { + RENDER_BACKEND.vertexAttribIPointer(index, a.size, a.type, a.stride, a.offset); + } else { + RENDER_BACKEND.vertexAttribPointer(index, a.size, a.type, a.normalized, a.stride, a.offset); + } + } + + private static void repointAttrib(GLContextState glCtx, int index) { + final int savedVBO = glCtx.boundVBO; + pointAttrib(glCtx, index); + if (glCtx.boundVBO != savedVBO) glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO); } public static void glVertexPointer(int size, int stride, IntBuffer pointer) { @@ -3125,40 +3441,40 @@ public class GLStateManager { } public static void glVertexPointer(int size, int stride, FloatBuffer pointer) { - ffpClientArrayPointer(Usage.POSITION.getAttributeLocation(), size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer)); + ffpClientArrayPointer(VAOManager.CONV_POSITION, size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glVertexPointer(int size, int stride, DoubleBuffer pointer) { - ffpClientArrayPointer(Usage.POSITION.getAttributeLocation(), size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer)); + ffpClientArrayPointer(VAOManager.CONV_POSITION, size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glVertexPointer(int size, int type, int stride, ByteBuffer pointer) { - ffpClientArrayPointer(Usage.POSITION.getAttributeLocation(), size, type, false, stride, pointer); + ffpClientArrayPointer(VAOManager.CONV_POSITION, size, type, false, stride, pointer); } public static void glVertexPointer(int size, int type, int stride, long pointer_buffer_offset) { - ffpClientArrayPointer(Usage.POSITION.getAttributeLocation(), size, type, false, stride, pointer_buffer_offset); + ffpClientArrayPointer(VAOManager.CONV_POSITION, size, type, false, stride, pointer_buffer_offset); } public static void glColorPointer(int size, int stride, FloatBuffer pointer) { - ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, GL11.GL_FLOAT, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer)); + ffpClientArrayPointer(VAOManager.CONV_COLOR, size, GL11.GL_FLOAT, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glColorPointer(int size, int stride, DoubleBuffer pointer) { - ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, GL11.GL_DOUBLE, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer)); + ffpClientArrayPointer(VAOManager.CONV_COLOR, size, GL11.GL_DOUBLE, Usage.COLOR.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glColorPointer(int size, int type, int stride, ByteBuffer pointer) { - ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer); + ffpClientArrayPointer(VAOManager.CONV_COLOR, size, type, Usage.COLOR.isNormalized(), stride, pointer); } public static void glColorPointer(int size, boolean unsigned, int stride, ByteBuffer pointer) { final int type = unsigned ? GL11.GL_UNSIGNED_BYTE : GL11.GL_BYTE; - ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer); + ffpClientArrayPointer(VAOManager.CONV_COLOR, size, type, Usage.COLOR.isNormalized(), stride, pointer); } public static void glColorPointer(int size, int type, int stride, long pointer_buffer_offset) { - ffpClientArrayPointer(Usage.COLOR.getAttributeLocation(), size, type, Usage.COLOR.isNormalized(), stride, pointer_buffer_offset); + ffpClientArrayPointer(VAOManager.CONV_COLOR, size, type, Usage.COLOR.isNormalized(), stride, pointer_buffer_offset); } public static void glNormalPointer(int stride, FloatBuffer pointer) { @@ -3178,33 +3494,31 @@ public class GLStateManager { } public static void glNormalPointer(int type, int stride, ByteBuffer pointer) { - ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, type, Usage.NORMAL.isNormalized(), stride, pointer); + ffpClientArrayPointer(VAOManager.CONV_NORMAL, 3, type, Usage.NORMAL.isNormalized(), stride, pointer); } public static void glNormalPointer(int type, int stride, FloatBuffer pointer) { - ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_FLOAT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer)); + ffpClientArrayPointer(VAOManager.CONV_NORMAL, 3, GL11.GL_FLOAT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glNormalPointer(int type, int stride, ShortBuffer pointer) { - ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_SHORT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer)); + ffpClientArrayPointer(VAOManager.CONV_NORMAL, 3, GL11.GL_SHORT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glNormalPointer(int type, int stride, IntBuffer pointer) { - ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_INT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer)); + ffpClientArrayPointer(VAOManager.CONV_NORMAL, 3, GL11.GL_INT, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glNormalPointer(int type, int stride, DoubleBuffer pointer) { - ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, GL11.GL_DOUBLE, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer)); + ffpClientArrayPointer(VAOManager.CONV_NORMAL, 3, GL11.GL_DOUBLE, Usage.NORMAL.isNormalized(), stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glNormalPointer(int type, int stride, long pointer_buffer_offset) { - ffpClientArrayPointer(Usage.NORMAL.getAttributeLocation(), 3, type, Usage.NORMAL.isNormalized(), stride, pointer_buffer_offset); + ffpClientArrayPointer(VAOManager.CONV_NORMAL, 3, type, Usage.NORMAL.isNormalized(), stride, pointer_buffer_offset); } public static void glTexCoordPointer(int size, int type, int stride, ByteBuffer pointer) { - final int loc = texCoordAttributeLocation(); - if (loc < 0) return; - ffpClientArrayPointer(loc, size, type, false, stride, pointer); + ffpClientArrayPointer(texCoordConventional(ctx()), size, type, false, stride, pointer); } public static void glTexCoordPointer(int size, int stride, FloatBuffer pointer) { @@ -3224,47 +3538,63 @@ public class GLStateManager { } public static void glTexCoordPointer(int size, int type, int stride, FloatBuffer pointer) { - final int loc = texCoordAttributeLocation(); - if (loc < 0) return; - ffpClientArrayPointer(loc, size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer)); + ffpClientArrayPointer(texCoordConventional(ctx()), size, GL11.GL_FLOAT, false, stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glTexCoordPointer(int size, int type, int stride, ShortBuffer pointer) { - final int loc = texCoordAttributeLocation(); - if (loc < 0) return; - ffpClientArrayPointer(loc, size, GL11.GL_SHORT, false, stride, MemoryUtilities.memByteBuffer(pointer)); + ffpClientArrayPointer(texCoordConventional(ctx()), size, GL11.GL_SHORT, false, stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glTexCoordPointer(int size, int type, int stride, IntBuffer pointer) { - final int loc = texCoordAttributeLocation(); - if (loc < 0) return; - ffpClientArrayPointer(loc, size, GL11.GL_INT, false, stride, MemoryUtilities.memByteBuffer(pointer)); + ffpClientArrayPointer(texCoordConventional(ctx()), size, GL11.GL_INT, false, stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glTexCoordPointer(int size, int type, int stride, DoubleBuffer pointer) { - final int loc = texCoordAttributeLocation(); - if (loc < 0) return; - ffpClientArrayPointer(loc, size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer)); + ffpClientArrayPointer(texCoordConventional(ctx()), size, GL11.GL_DOUBLE, false, stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glTexCoordPointer(int size, int type, int stride, long pointer_buffer_offset) { - final int loc = texCoordAttributeLocation(); - if (loc < 0) return; - ffpClientArrayPointer(loc, size, type, false, stride, pointer_buffer_offset); + ffpClientArrayPointer(texCoordConventional(ctx()), size, type, false, stride, pointer_buffer_offset); } public static void glEnableClientState(int cap) { - final int location = clientStateToAttributeLocation(cap); - if (location >= 0) glEnableVertexAttribArray(location); - final int flag = clientStateToVertexFlag(cap); - if (flag != 0) ctx().vaos.enableClientVertexFlag(flag); + setClientArrayCap(ctx(), cap, true); } public static void glDisableClientState(int cap) { - final int location = clientStateToAttributeLocation(cap); - if (location >= 0) glDisableVertexAttribArray(location); - final int flag = clientStateToVertexFlag(cap); - if (flag != 0) ctx().vaos.disableClientVertexFlag(flag); + setClientArrayCap(ctx(), cap, false); + } + + private static boolean setClientArrayCap(GLContextState glCtx, int cap, boolean enabled) { + final int conv = clientStateToConventional(glCtx, cap); + if (conv < 0) return false; + setClientArrayEnabled(glCtx, conv, enabled); + return true; + } + + private static void setClientArrayEnabled(GLContextState glCtx, int conv, boolean enabled) { + final VAOManager vaos = glCtx.vaos; + final boolean repoint = vaos.setConventionalEnabled(conv, enabled); + final int index = VAOManager.conventionalLocation(conv); + if (index >= 0) { + if (repoint) repointAttrib(glCtx, index); + syncAttribEnable(glCtx, index); + } + final int flag = conventionalVertexFlag(conv); + if (flag == 0) return; + if (enabled) { + vaos.enableClientVertexFlag(flag); + } else { + vaos.disableClientVertexFlag(flag); + } + } + + private static void syncAttribEnable(GLContextState glCtx, int index) { + if (glCtx.vaos.isAttribEnabled(index)) { + RENDER_BACKEND.enableVertexAttribArray(index); + } else { + RENDER_BACKEND.disableVertexAttribArray(index); + } } public static void glVertexAttrib2f(int index, float v0, float v1) { @@ -3290,9 +3620,11 @@ public class GLStateManager { } public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long offset) { + if (!isLegalVertexAttrib(index, size, type, stride, false)) return; final GLContextState glCtx = ctx(); - glCtx.vaos.setAttribute(index, size, type, normalized, stride, offset, glCtx.boundVBO); - RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, offset); + if (glCtx.vaos.setAttribute(index, size, type, normalized, stride, offset, glCtx.boundVBO)) { + RENDER_BACKEND.vertexAttribPointer(index, size, type, normalized, stride, offset); + } } public static void glVertexAttribPointer(int index, int size, boolean normalized, int stride, FloatBuffer pointer) { @@ -3300,88 +3632,95 @@ public class GLStateManager { } public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, FloatBuffer pointer) { - ctx().vaos.setAttribute(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer)); + glVertexAttribPointer(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, DoubleBuffer pointer) { - ctx().vaos.setAttribute(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer)); + glVertexAttribPointer(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, IntBuffer pointer) { - ctx().vaos.setAttribute(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer)); + glVertexAttribPointer(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ShortBuffer pointer) { - ctx().vaos.setAttribute(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer)); + glVertexAttribPointer(index, size, type, normalized, stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) { + if (!isLegalVertexAttrib(index, size, type, stride, false)) return; ctx().vaos.setAttribute(index, size, type, normalized, stride, pointer); } public static void glVertexAttribIPointer(int index, int size, int type, int stride, long offset) { + if (!isLegalVertexAttrib(index, size, type, stride, true)) return; final GLContextState glCtx = ctx(); - glCtx.vaos.setAttribute(index, size, type, false, stride, offset, glCtx.boundVBO); - RENDER_BACKEND.vertexAttribIPointer(index, size, type, stride, offset); + if (glCtx.vaos.setIntegerAttribute(index, size, type, stride, offset, glCtx.boundVBO)) { + RENDER_BACKEND.vertexAttribIPointer(index, size, type, stride, offset); + } } public static void glVertexAttribIPointer(int index, int size, int type, int stride, ByteBuffer pointer) { - ctx().vaos.setAttribute(index, size, type, false, stride, pointer); + if (!isLegalVertexAttrib(index, size, type, stride, true)) return; + ctx().vaos.setIntegerAttribute(index, size, type, stride, pointer); } public static void glVertexAttribIPointer(int index, int size, int type, int stride, IntBuffer pointer) { - ctx().vaos.setAttribute(index, size, type, false, stride, MemoryUtilities.memByteBuffer(pointer)); + glVertexAttribIPointer(index, size, type, stride, MemoryUtilities.memByteBuffer(pointer)); } public static void glVertexAttribIPointer(int index, int size, int type, int stride, ShortBuffer pointer) { - ctx().vaos.setAttribute(index, size, type, false, stride, MemoryUtilities.memByteBuffer(pointer)); + glVertexAttribIPointer(index, size, type, stride, MemoryUtilities.memByteBuffer(pointer)); } - public static void glVertexAttribDivisor(int index, int divisor) { RENDER_BACKEND.vertexAttribDivisor(index, divisor); } - public static void glVertexAttribDivisorARB(int index, int divisor) { RENDER_BACKEND.vertexAttribDivisor(index, divisor); } + public static void glVertexAttribDivisor(int index, int divisor) { + if (ctx().vaos.setDivisor(index, divisor)) RENDER_BACKEND.vertexAttribDivisor(index, divisor); + } + + public static void glVertexAttribDivisorARB(int index, int divisor) { glVertexAttribDivisor(index, divisor); } public static void glBindVertexBuffer(int bindingindex, int buffer, long offset, int stride) { RENDER_BACKEND.bindVertexBuffer(bindingindex, buffer, offset, stride); } public static void glVertexAttribFormat(int attribindex, int size, int type, boolean normalized, int relativeoffset) { RENDER_BACKEND.vertexAttribFormat(attribindex, size, type, normalized, relativeoffset); } public static void glVertexAttribIFormat(int attribindex, int size, int type, int relativeoffset) { RENDER_BACKEND.vertexAttribIFormat(attribindex, size, type, relativeoffset); } public static void glVertexAttribBinding(int attribindex, int bindingindex) { RENDER_BACKEND.vertexAttribBinding(attribindex, bindingindex); } public static void glEnableVertexAttribArray(int index) { - ctx().vaos.enableAttribute(index); + if (!isValidVertexAttribIndex(index, "glEnableVertexAttribArray")) return; + final GLContextState glCtx = ctx(); + if (glCtx.vaos.enableAttribute(index)) repointAttrib(glCtx, index); RENDER_BACKEND.enableVertexAttribArray(index); } public static void glDisableVertexAttribArray(int index) { - ctx().vaos.disableAttribute(index); - RENDER_BACKEND.disableVertexAttribArray(index); + if (!isValidVertexAttribIndex(index, "glDisableVertexAttribArray")) return; + final GLContextState glCtx = ctx(); + if (glCtx.vaos.disableAttribute(index)) repointAttrib(glCtx, index); + if (!glCtx.vaos.isAttribEnabled(index)) RENDER_BACKEND.disableVertexAttribArray(index); } - private static int clientStateToAttributeLocation(int cap) { + private static int clientStateToConventional(GLContextState glCtx, int cap) { return switch (cap) { - case GL11.GL_VERTEX_ARRAY -> Usage.POSITION.getAttributeLocation(); - case GL11.GL_COLOR_ARRAY -> Usage.COLOR.getAttributeLocation(); - case GL11.GL_NORMAL_ARRAY -> Usage.NORMAL.getAttributeLocation(); - case GL11.GL_TEXTURE_COORD_ARRAY -> texCoordAttributeLocation(); + case GL11.GL_VERTEX_ARRAY -> VAOManager.CONV_POSITION; + case GL11.GL_NORMAL_ARRAY -> VAOManager.CONV_NORMAL; + case GL11.GL_COLOR_ARRAY -> VAOManager.CONV_COLOR; + case GL11.GL_INDEX_ARRAY -> VAOManager.CONV_INDEX; + case GL11.GL_EDGE_FLAG_ARRAY -> VAOManager.CONV_EDGE_FLAG; + case GL14.GL_SECONDARY_COLOR_ARRAY -> VAOManager.CONV_SECONDARY_COLOR; + case GL14.GL_FOG_COORDINATE_ARRAY -> VAOManager.CONV_FOG_COORD; + case GL11.GL_TEXTURE_COORD_ARRAY -> texCoordConventional(glCtx); default -> -1; }; } - private static int clientStateToVertexFlag(int cap) { - return switch (cap) { - case GL11.GL_COLOR_ARRAY -> VertexFlags.COLOR_BIT; - case GL11.GL_NORMAL_ARRAY -> VertexFlags.NORMAL_BIT; - case GL11.GL_TEXTURE_COORD_ARRAY -> switch (ctx().clientActiveTextureUnit) { - case 0 -> VertexFlags.TEXTURE_BIT; - case 1 -> VertexFlags.BRIGHTNESS_BIT; - default -> 0; - }; - default -> 0; // GL_VERTEX_ARRAY - position is implicit + private static int conventionalVertexFlag(int conv) { + return switch (conv) { + case VAOManager.CONV_COLOR -> VertexFlags.COLOR_BIT; + case VAOManager.CONV_NORMAL -> VertexFlags.NORMAL_BIT; + case VAOManager.CONV_TEX0 -> VertexFlags.TEXTURE_BIT; + case VAOManager.CONV_TEX0 + 1 -> VertexFlags.BRIGHTNESS_BIT; + default -> 0; }; } - private static boolean isPositionArrayEnabled(GLContextState glCtx) { - final VAOManager.Attrib position = glCtx.vaos.attrib(Usage.POSITION.getAttributeLocation()); - return position != null && position.enabled; - } - static final int CLIENT_ATTRIB_STACK_DEPTH = 16; public static void glPushClientAttrib(int mask) { final GLContextState glCtx = ctx(); @@ -3395,9 +3734,13 @@ public class GLStateManager { glCtx.clientAttribSavedUnpackBuffer[sp] = glCtx.boundPixelUnpackBuffer; } if ((mask & GL11.GL_CLIENT_VERTEX_ARRAY_BIT) != 0) { - glCtx.clientAttribSavedTextureUnit[sp] = glCtx.clientActiveTextureUnit; - glCtx.clientAttribSavedVertexFlags[sp] = glCtx.vaos.getVertexFlags(); - glCtx.clientAttribSavedVertexArray[sp] = isPositionArrayEnabled(glCtx); + VAOManager.ClientArrays saved = glCtx.clientAttribSavedArrays[sp]; + if (saved == null) glCtx.clientAttribSavedArrays[sp] = saved = new VAOManager.ClientArrays(); + saved.boundVAO = glCtx.boundVAO; + saved.boundVBO = glCtx.boundVBO; + saved.boundEBO = glCtx.vaos.boundEBO; + saved.clientActiveTextureUnit = glCtx.clientActiveTextureUnit; + glCtx.vaos.saveClientArrays(saved); } glCtx.clientAttribStackPointer = sp + 1; } @@ -3423,25 +3766,42 @@ public class GLStateManager { if (glCtx.boundPixelUnpackBuffer != unpackBuffer) glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, unpackBuffer); } if ((mask & GL11.GL_CLIENT_VERTEX_ARRAY_BIT) != 0) { - glCtx.clientActiveTextureUnit = glCtx.clientAttribSavedTextureUnit[sp]; - glCtx.vaos.setVertexFlags(glCtx.clientAttribSavedVertexFlags[sp]); - final boolean vertexArray = glCtx.clientAttribSavedVertexArray[sp]; - if (isPositionArrayEnabled(glCtx) != vertexArray) { - if (vertexArray) { - glEnableClientState(GL11.GL_VERTEX_ARRAY); - } else { - glDisableClientState(GL11.GL_VERTEX_ARRAY); - } - } + restoreClientArrays(glCtx, glCtx.clientAttribSavedArrays[sp]); } } } - public static void glInterleavedArrays(int format, int stride, long pointer) { - if (!ctx().ffp.isActive()) { - return; + private static void restoreClientArrays(GLContextState glCtx, VAOManager.ClientArrays saved) { + final VAOManager vaos = glCtx.vaos; + if (!vaos.vaoMap.containsKey(saved.boundVAO)) return; + bindVertexArrayInternal(glCtx, saved.boundVAO); + glCtx.clientActiveTextureUnit = saved.clientActiveTextureUnit; + if (vaos.isBufferLive(saved.boundVBO, saved)) { + final int changed = vaos.restoreClientArrays(saved); + for (int pointers = changed & 0xFFFF; pointers != 0; pointers &= pointers - 1) { + pointAttrib(glCtx, Integer.numberOfTrailingZeros(pointers)); + } + for (int enables = changed >>> 16; enables != 0; enables &= enables - 1) { + syncAttribEnable(glCtx, Integer.numberOfTrailingZeros(enables)); + } + if (glCtx.boundVBO != saved.boundVBO) glBindBuffer(GL15.GL_ARRAY_BUFFER, saved.boundVBO); } + if (vaos.boundEBO != saved.boundEBO && vaos.isBufferLive(saved.boundEBO, saved)) glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, saved.boundEBO); + } + public static void glInterleavedArrays(int format, int stride, long pointer) { + interleavedArrays(format, stride, pointer, null); + } + + private static void interleavedPointer(int conv, int size, int type, boolean normalized, int stride, long pointer, ByteBuffer base, int offset) { + if (base == null) { + ffpClientArrayPointer(conv, size, type, normalized, stride, pointer + offset); + } else { + ffpClientArrayPointer(conv, size, type, normalized, stride, MemoryUtilities.memSlice(base, offset, base.remaining() - offset)); + } + } + + private static void interleavedArrays(int format, int stride, long pointer, ByteBuffer base) { // Mesa _mesa_get_interleaved_layout decomposition final int f = 4; // sizeof(float) final int c = f * ((4 + (f - 1)) / f); // 4 ubytes padded to float alignment = 4 @@ -3504,47 +3864,47 @@ public class GLStateManager { if (tflag) { glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(tcomps, GL11.GL_FLOAT, stride, pointer + toffset); + interleavedPointer(texCoordConventional(ctx()), tcomps, GL11.GL_FLOAT, false, stride, pointer, base, toffset); } else { glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY); } if (cflag) { glEnableClientState(GL11.GL_COLOR_ARRAY); - glColorPointer(ccomps, ctype, stride, pointer + coffset); + interleavedPointer(VAOManager.CONV_COLOR, ccomps, ctype, Usage.COLOR.isNormalized(), stride, pointer, base, coffset); } else { glDisableClientState(GL11.GL_COLOR_ARRAY); } if (nflag) { glEnableClientState(GL11.GL_NORMAL_ARRAY); - glNormalPointer(GL11.GL_FLOAT, stride, pointer + noffset); + interleavedPointer(VAOManager.CONV_NORMAL, 3, GL11.GL_FLOAT, Usage.NORMAL.isNormalized(), stride, pointer, base, noffset); } else { glDisableClientState(GL11.GL_NORMAL_ARRAY); } glEnableClientState(GL11.GL_VERTEX_ARRAY); - glVertexPointer(vcomps, GL11.GL_FLOAT, stride, pointer + voffset); + interleavedPointer(VAOManager.CONV_POSITION, vcomps, GL11.GL_FLOAT, false, stride, pointer, base, voffset); } public static void glInterleavedArrays(int format, int stride, ByteBuffer pointer) { - glInterleavedArrays(format, stride, MemoryUtilities.memAddress0(pointer)); + interleavedArrays(format, stride, 0L, pointer); } public static void glInterleavedArrays(int format, int stride, FloatBuffer pointer) { - glInterleavedArrays(format, stride, MemoryUtilities.memAddress0(MemoryUtilities.memByteBuffer(pointer))); + interleavedArrays(format, stride, 0L, MemoryUtilities.memByteBuffer(pointer)); } public static void glInterleavedArrays(int format, int stride, DoubleBuffer pointer) { - glInterleavedArrays(format, stride, MemoryUtilities.memAddress0(MemoryUtilities.memByteBuffer(pointer))); + interleavedArrays(format, stride, 0L, MemoryUtilities.memByteBuffer(pointer)); } public static void glInterleavedArrays(int format, int stride, IntBuffer pointer) { - glInterleavedArrays(format, stride, MemoryUtilities.memAddress0(MemoryUtilities.memByteBuffer(pointer))); + interleavedArrays(format, stride, 0L, MemoryUtilities.memByteBuffer(pointer)); } public static void glInterleavedArrays(int format, int stride, ShortBuffer pointer) { - glInterleavedArrays(format, stride, MemoryUtilities.memAddress0(MemoryUtilities.memByteBuffer(pointer))); + interleavedArrays(format, stride, 0L, MemoryUtilities.memByteBuffer(pointer)); } public static void glLogicOp(int opcode) { @@ -4434,7 +4794,8 @@ public class GLStateManager { RENDER_BACKEND.depthRange(glCtx.viewportState.depthRangeNear, glCtx.viewportState.depthRangeFar); RENDER_BACKEND.scissor(glCtx.scissorState.x, glCtx.scissorState.y, glCtx.scissorState.width, glCtx.scissorState.height); RENDER_BACKEND.lineWidth(Math.clamp(glCtx.lineState.getWidth(), lineWidthMin, lineWidthMax)); - RENDER_BACKEND.pointSize(glCtx.pointState.getSize()); + applyPointState(glCtx); + applyHintState(glCtx); RENDER_BACKEND.polygonMode(GL11.GL_FRONT_AND_BACK, glCtx.polygonState.getFrontMode()); applyPolygonOffset(glCtx); @@ -4556,7 +4917,12 @@ public class GLStateManager { } if ((restore & StateSet.R_POINT) != 0 && (changed & StateSet.R_POINT) != 0) { attribValueRestores++; - RENDER_BACKEND.pointSize(glCtx.pointState.getSize()); + applyPointState(glCtx); + attribBackendCalls++; + } + if ((restore & StateSet.R_HINT) != 0 && (changed & StateSet.R_HINT) != 0) { + attribValueRestores++; + applyHintState(glCtx); attribBackendCalls++; } if ((restore & StateSet.R_POLYGON) != 0 && (changed & StateSet.R_POLYGON) != 0) { @@ -5493,6 +5859,14 @@ public class GLStateManager { } public static void glTexStorage2D(int target, int levels, int internalFormat, int width, int height) { RENDER_BACKEND.texStorage2D(target, levels, internalFormat, width, height); + if (target == GL11.GL_TEXTURE_2D) { + final boolean locked = acquireDrawLock(); + try { + unshrinkableTextures.add(getBoundTextureForServerState()); + } finally { + if (locked) releaseDrawLock(); + } + } TextureInfoCache.INSTANCE.onTexStorage2D(target, internalFormat, width, height); } public static void glClearTexImage(int texture, int level, int format, int type, ByteBuffer data) { RENDER_BACKEND.clearTexImage(texture, level, format, type); } @@ -6418,11 +6792,8 @@ public class GLStateManager { } public static void glHint(int target, int hint) { - // These hint targets are removed in core profile, ignore them - switch (target) { - case GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_POINT_SMOOTH_HINT, GL11.GL_FOG_HINT, GL14.GL_GENERATE_MIPMAP_HINT -> { - return; - } + if (hint != GL11.GL_NICEST && hint != GL11.GL_FASTEST && hint != GL11.GL_DONT_CARE) { + return; } final RecordMode mode = DisplayListManager.getRecordMode(); if (mode != RecordMode.NONE) { @@ -6431,7 +6802,16 @@ public class GLStateManager { return; } } - RENDER_BACKEND.hint(target, hint); + final int shift = hintShift(target); + if (shift >= 0) { + final IntegerStateStack hints = ctx().hintState; + final int code = hint == GL11.GL_FASTEST ? 1 : hint == GL11.GL_NICEST ? 2 : 0; + final int packed = hints.getValue() & ~(3 << shift) | code << shift; + if (packed != hints.getValue()) mod(hints).setValue(packed); + } + if (!isLegacyHint(target)) { + RENDER_BACKEND.hint(target, hint); + } } public static void glLineStipple(int factor, short pattern) { @@ -7370,9 +7750,10 @@ public class GLStateManager { // Since the vao needs to be bound to do stuff like state setup & data upload, it'll still execute the bind call. // This is technically wrong, but I'm not sure if there's a better solution here. } - if (array == 0) { - array = glCtx.defaultVAO; - } + bindVertexArrayInternal(glCtx, array == 0 ? glCtx.defaultVAO : array); + } + + private static void bindVertexArrayInternal(GLContextState glCtx, int array) { final boolean locked = acquireDrawLock(); try { if (glCtx.boundVAO != array) { @@ -7392,9 +7773,7 @@ public class GLStateManager { glCtx.vaos.onDeleteVertexArray(array); if (array == glCtx.boundVAO) { // Deleting the bound VAO implicitly unbinds it. Rebind the default VAO. - glCtx.boundVAO = glCtx.defaultVAO; - glCtx.vaos.onBindVertexArrayPre(glCtx.defaultVAO); - RENDER_BACKEND.bindVertexArray(glCtx.defaultVAO); + bindVertexArrayInternal(glCtx, glCtx.defaultVAO); } RENDER_BACKEND.deleteVertexArrays(array); } finally { @@ -7554,9 +7933,10 @@ public class GLStateManager { if (params.length > 1) params[1] = polygon.getBackMode(); } default -> { + final GLContextState glCtx = ctx(); if (!HAS_MULTIPLE_SET.contains(pname)) { - params[0] = glGetInteger(pname); - } else { + params[0] = glGetInteger(glCtx, pname); + } else if (!LegacyQueries.getIntegerv(glCtx, pname, params)) { RENDER_BACKEND.getInteger(pname, params); } } @@ -8782,6 +9162,22 @@ public class GLStateManager { return ctx().pointState; } + public static IntegerStateStack getHintState() { + return ctx().hintState; + } + + public static BooleanStateStack getPointSpriteState() { + return ctx().pointSpriteState; + } + + public static BooleanStateStack getVertexProgramTwoSideState() { + return ctx().vertexProgramTwoSideState; + } + + public static BooleanStateStack getEdgeFlagState() { + return ctx().edgeFlagState; + } + public static PolygonStateStack getPolygonState() { return ctx().polygonState; } diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/LegacyQueries.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/LegacyQueries.java new file mode 100644 index 000000000..d2bb50fbb --- /dev/null +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/LegacyQueries.java @@ -0,0 +1,167 @@ +package com.gtnewhorizons.angelica.glsm; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL14; +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL20; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +final class LegacyQueries { + + static final int BOOL = 1; + static final int INT = 2; + static final int FLOAT = 3; + static final int FLOATN = 4; + + private static final int COUNT_SHIFT = 3; + + private LegacyQueries() {} + + static int desc(int pname) { + return switch (pname) { + case GL11.GL_ALPHA_TEST, GL11.GL_AUTO_NORMAL, GL11.GL_COLOR_MATERIAL, GL14.GL_COLOR_SUM, GL11.GL_FOG, + GL11.GL_INDEX_LOGIC_OP, GL11.GL_LIGHTING, GL11.GL_LIGHT0, GL11.GL_LIGHT1, GL11.GL_LIGHT2, GL11.GL_LIGHT3, + GL11.GL_LIGHT4, GL11.GL_LIGHT5, GL11.GL_LIGHT6, GL11.GL_LIGHT7, GL11.GL_LINE_STIPPLE, GL11.GL_MAP1_COLOR_4, + GL11.GL_MAP1_INDEX, GL11.GL_MAP1_NORMAL, GL11.GL_MAP1_TEXTURE_COORD_1, GL11.GL_MAP1_TEXTURE_COORD_2, + GL11.GL_MAP1_TEXTURE_COORD_3, GL11.GL_MAP1_TEXTURE_COORD_4, GL11.GL_MAP1_VERTEX_3, GL11.GL_MAP1_VERTEX_4, + GL11.GL_MAP2_COLOR_4, GL11.GL_MAP2_INDEX, GL11.GL_MAP2_NORMAL, GL11.GL_MAP2_TEXTURE_COORD_1, + GL11.GL_MAP2_TEXTURE_COORD_2, GL11.GL_MAP2_TEXTURE_COORD_3, GL11.GL_MAP2_TEXTURE_COORD_4, + GL11.GL_MAP2_VERTEX_3, GL11.GL_MAP2_VERTEX_4, GL11.GL_NORMALIZE, GL11.GL_POINT_SMOOTH, + GL11.GL_POLYGON_STIPPLE, GL12.GL_RESCALE_NORMAL, GL11.GL_TEXTURE_1D, GL12.GL_TEXTURE_3D, + GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_GEN_S, GL11.GL_TEXTURE_GEN_T, GL11.GL_TEXTURE_GEN_R, + GL11.GL_TEXTURE_GEN_Q, GL11.GL_LIGHT_MODEL_LOCAL_VIEWER, GL11.GL_LIGHT_MODEL_TWO_SIDE, + GL11.GL_VERTEX_ARRAY, GL11.GL_NORMAL_ARRAY, GL11.GL_COLOR_ARRAY, GL11.GL_TEXTURE_COORD_ARRAY, + GL11.GL_INDEX_ARRAY, GL11.GL_EDGE_FLAG_ARRAY, GL14.GL_SECONDARY_COLOR_ARRAY, GL14.GL_FOG_COORDINATE_ARRAY, + GL11.GL_EDGE_FLAG, GL20.GL_POINT_SPRITE, GL20.GL_VERTEX_PROGRAM_TWO_SIDE -> BOOL; + case GL11.GL_ALPHA_TEST_FUNC, GL11.GL_LIST_BASE, GL11.GL_MATRIX_MODE, GL11.GL_SHADE_MODEL, + GL11.GL_COLOR_MATERIAL_FACE, GL11.GL_COLOR_MATERIAL_PARAMETER, GL12.GL_LIGHT_MODEL_COLOR_CONTROL, + GL11.GL_FOG_MODE, GL11.GL_LINE_STIPPLE_PATTERN, GL11.GL_LINE_STIPPLE_REPEAT, GL13.GL_CLIENT_ACTIVE_TEXTURE, + GL20.GL_MAX_TEXTURE_COORDS, GL11.GL_RENDER_MODE, GL11.GL_FOG_HINT, GL11.GL_PERSPECTIVE_CORRECTION_HINT, + GL11.GL_POINT_SMOOTH_HINT, GL14.GL_GENERATE_MIPMAP_HINT, GL11.GL_LINE_SMOOTH_HINT, GL11.GL_POLYGON_SMOOTH_HINT, + GL13.GL_TEXTURE_COMPRESSION_HINT, GL20.GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL20.GL_POINT_SPRITE_COORD_ORIGIN, + GL11.GL_VERTEX_ARRAY_SIZE, GL11.GL_VERTEX_ARRAY_TYPE, GL11.GL_VERTEX_ARRAY_STRIDE, GL15.GL_VERTEX_ARRAY_BUFFER_BINDING, + GL11.GL_NORMAL_ARRAY_TYPE, GL11.GL_NORMAL_ARRAY_STRIDE, GL15.GL_NORMAL_ARRAY_BUFFER_BINDING, + GL11.GL_COLOR_ARRAY_SIZE, GL11.GL_COLOR_ARRAY_TYPE, GL11.GL_COLOR_ARRAY_STRIDE, GL15.GL_COLOR_ARRAY_BUFFER_BINDING, + GL11.GL_INDEX_ARRAY_TYPE, GL11.GL_INDEX_ARRAY_STRIDE, GL15.GL_INDEX_ARRAY_BUFFER_BINDING, + GL11.GL_TEXTURE_COORD_ARRAY_SIZE, GL11.GL_TEXTURE_COORD_ARRAY_TYPE, GL11.GL_TEXTURE_COORD_ARRAY_STRIDE, + GL15.GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, GL11.GL_EDGE_FLAG_ARRAY_STRIDE, GL15.GL_EDGE_FLAG_ARRAY_BUFFER_BINDING, + GL14.GL_SECONDARY_COLOR_ARRAY_SIZE, GL14.GL_SECONDARY_COLOR_ARRAY_TYPE, GL14.GL_SECONDARY_COLOR_ARRAY_STRIDE, + GL15.GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING, GL14.GL_FOG_COORDINATE_ARRAY_TYPE, GL14.GL_FOG_COORDINATE_ARRAY_STRIDE, + GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING -> INT; + case GL11.GL_FOG_DENSITY, GL11.GL_FOG_START, GL11.GL_FOG_END, GL14.GL_POINT_SIZE_MIN, GL14.GL_POINT_SIZE_MAX, + GL14.GL_POINT_FADE_THRESHOLD_SIZE -> FLOAT; + case GL11.GL_ALPHA_TEST_REF -> FLOATN; + case GL14.GL_POINT_DISTANCE_ATTENUATION -> FLOAT | 2 << COUNT_SHIFT; + case GL11.GL_CURRENT_TEXTURE_COORDS -> FLOAT | 3 << COUNT_SHIFT; + case GL11.GL_MODELVIEW_MATRIX, GL11.GL_PROJECTION_MATRIX, GL11.GL_TEXTURE_MATRIX -> FLOATN | 15 << COUNT_SHIFT; + case GL11.GL_CURRENT_NORMAL -> FLOATN | 2 << COUNT_SHIFT; + case GL11.GL_CURRENT_COLOR, GL14.GL_CURRENT_SECONDARY_COLOR, GL11.GL_FOG_COLOR, GL11.GL_LIGHT_MODEL_AMBIENT -> FLOATN | 3 << COUNT_SHIFT; + default -> 0; + }; + } + + static int type(int desc) { + return desc & 7; + } + + static int count(int desc) { + return (desc >> COUNT_SHIFT) + 1; + } + + static int floatToInt(float value) { + return (int) (2147483647.0 * value); + } + + static int lround(float value) { + return value < 0 ? -Math.round(-value) : Math.round(value); + } + + static int toInt(int type, float value) { + return type == FLOATN ? floatToInt(value) : lround(value); + } + + private static FloatBuffer fillScratch(GLContextState glCtx, int pname) { + final FloatBuffer scratch = glCtx.queryScratch; + scratch.clear(); + GLStateManager.glGetFloat(pname, scratch); + return scratch; + } + + private static IllegalStateException uncached(int pname) { + return new IllegalStateException("Legacy pname 0x" + Integer.toHexString(pname) + " has no cached answer for its declared type"); + } + + static int getInteger(GLContextState glCtx, int pname, int desc) { + final int type = type(desc); + if (count(desc) > 1) { + return toInt(type, fillScratch(glCtx, pname).get(0)); + } + return switch (type) { + case BOOL -> GLStateManager.glGetBoolean(glCtx, pname) ? GL11.GL_TRUE : GL11.GL_FALSE; + case FLOAT -> lround(GLStateManager.glGetFloat(glCtx, pname)); + case FLOATN -> floatToInt(GLStateManager.glGetFloat(glCtx, pname)); + default -> throw uncached(pname); + }; + } + + static float getFloat(GLContextState glCtx, int pname, int desc) { + if (count(desc) > 1) { + return fillScratch(glCtx, pname).get(0); + } + return switch (type(desc)) { + case BOOL -> GLStateManager.glGetBoolean(glCtx, pname) ? 1.0f : 0.0f; + case INT -> (float) GLStateManager.glGetInteger(glCtx, pname); + default -> throw uncached(pname); + }; + } + + static boolean getBoolean(GLContextState glCtx, int pname, int desc) { + if (count(desc) > 1) { + return fillScratch(glCtx, pname).get(0) != 0.0f; + } + return switch (type(desc)) { + case INT -> GLStateManager.glGetInteger(glCtx, pname) != 0; + case FLOAT, FLOATN -> GLStateManager.glGetFloat(glCtx, pname) != 0.0f; + default -> throw uncached(pname); + }; + } + + static boolean getIntegerv(GLContextState glCtx, int pname, IntBuffer params) { + final int desc = desc(pname); + if (desc == 0) return false; + final int pos = params.position(); + final int type = type(desc); + final FloatBuffer scratch = fillScratch(glCtx, pname); + for (int i = 0, count = count(desc); i < count; i++) { + params.put(pos + i, toInt(type, scratch.get(i))); + } + return true; + } + + static boolean getIntegerv(GLContextState glCtx, int pname, int[] params) { + final int desc = desc(pname); + if (desc == 0) return false; + final int type = type(desc); + final FloatBuffer scratch = fillScratch(glCtx, pname); + for (int i = 0, count = Math.min(count(desc), params.length); i < count; i++) { + params[i] = toInt(type, scratch.get(i)); + } + return true; + } + + static boolean getBooleanv(GLContextState glCtx, int pname, ByteBuffer params) { + final int desc = desc(pname); + if (desc == 0) return false; + final int pos = params.position(); + final FloatBuffer scratch = fillScratch(glCtx, pname); + for (int i = 0, count = count(desc); i < count; i++) { + params.put(pos + i, (byte) (scratch.get(i) != 0.0f ? GL11.GL_TRUE : GL11.GL_FALSE)); + } + return true; + } +} diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/StateSet.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/StateSet.java index 05910cfb4..f6ec54420 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/StateSet.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/StateSet.java @@ -24,6 +24,7 @@ public final class StateSet { public static final int R_ACTIVE_UNIT = 1 << 13; public static final int R_PROGRAM = 1 << 14; public static final int R_SCISSOR = 1 << 15; + public static final int R_HINT = 1 << 16; public static final int B_FRAG_COLORBUF = 1; public static final int B_FRAG_TEXTURE = 1 << 1; @@ -224,6 +225,9 @@ public final class StateSet { if ((mask & GL11.GL_POINT_BIT) != 0) { restore |= R_POINT; } + if ((mask & GL11.GL_HINT_BIT) != 0) { + restore |= R_HINT; + } if ((mask & GL11.GL_POLYGON_BIT) != 0) { restore |= R_POLYGON; } diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/VAOManager.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/VAOManager.java index 303da281c..888ba4430 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/VAOManager.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/ffp/VAOManager.java @@ -1,13 +1,20 @@ package com.gtnewhorizons.angelica.glsm.ffp; +import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage; import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.GLTypes; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL30; +import org.lwjgl.opengl.GL33; +import org.lwjgl.opengl.GL41; import javax.annotation.Nullable; import java.nio.ByteBuffer; +import java.util.Arrays; import static com.gtnewhorizons.angelica.glsm.backend.BackendManager.RENDER_BACKEND; @@ -19,12 +26,116 @@ public final class VAOManager { final Int2ObjectOpenHashMap eboOwners = new Int2ObjectOpenHashMap<>(); private VAOData current = null; + public static final int CONV_POSITION = 0; + public static final int CONV_NORMAL = 1; + public static final int CONV_COLOR = 2; + public static final int CONV_SECONDARY_COLOR = 3; + public static final int CONV_FOG_COORD = 4; + public static final int CONV_INDEX = 5; + public static final int CONV_EDGE_FLAG = 6; + public static final int CONV_TEX0 = 7; + public static final int TEX_COORD_UNITS = 8; + public static final int CONV_COUNT = CONV_TEX0 + TEX_COORD_UNITS; + + private static final int[] CONV_LOCATION = new int[CONV_COUNT]; + private static final int[] LOCATION_CONV = new int[MAX_ATTRIBS]; + static { + Arrays.fill(CONV_LOCATION, -1); + Arrays.fill(LOCATION_CONV, -1); + mapConventional(CONV_POSITION, Usage.POSITION); + mapConventional(CONV_NORMAL, Usage.NORMAL); + mapConventional(CONV_COLOR, Usage.COLOR); + mapConventional(CONV_TEX0, Usage.PRIMARY_UV); + mapConventional(CONV_TEX0 + 1, Usage.SECONDARY_UV); + } + + private static void mapConventional(int conv, Usage usage) { + final int location = usage.getAttributeLocation(); + CONV_LOCATION[conv] = location; + LOCATION_CONV[location] = conv; + } + + public static int conventionalLocation(int conv) { + return CONV_LOCATION[conv]; + } + + private static final int T_BYTE = 1, T_UBYTE = 1 << 1, T_SHORT = 1 << 2, T_USHORT = 1 << 3, T_INT = 1 << 4, T_UINT = 1 << 5, T_FLOAT = 1 << 6; + private static final int T_DOUBLE = 1 << 10, T_HALF = 1 << 11, T_FIXED = 1 << 12, T_PACKED = 1 << 13, T_PACKED_FLOAT = 1 << 14; + private static final int T_INTEGER = T_BYTE | T_UBYTE | T_SHORT | T_USHORT | T_INT | T_UINT; + private static final int T_COLOR = T_INTEGER | T_HALF | T_FLOAT | T_DOUBLE | T_PACKED; + private static final int T_GENERIC = T_COLOR | T_FIXED | T_PACKED_FLOAT; + private static final int[] LEGAL_TYPES = new int[CONV_COUNT]; + private static final int[] MIN_SIZE = new int[CONV_COUNT]; + private static final int[] MAX_SIZE = new int[CONV_COUNT]; + static { + legal(CONV_POSITION, T_SHORT | T_INT | T_FLOAT | T_DOUBLE | T_HALF | T_PACKED, 2, 4); + legal(CONV_NORMAL, T_BYTE | T_SHORT | T_INT | T_HALF | T_FLOAT | T_DOUBLE | T_PACKED, 3, 3); + legal(CONV_COLOR, T_COLOR, 3, 4); + legal(CONV_SECONDARY_COLOR, T_COLOR, 3, 4); + legal(CONV_FOG_COORD, T_HALF | T_FLOAT | T_DOUBLE, 1, 1); + legal(CONV_INDEX, T_UBYTE | T_SHORT | T_INT | T_FLOAT | T_DOUBLE, 1, 1); + legal(CONV_EDGE_FLAG, T_UBYTE, 1, 1); + for (int unit = 0; unit < TEX_COORD_UNITS; unit++) { + legal(CONV_TEX0 + unit, T_SHORT | T_INT | T_HALF | T_FLOAT | T_DOUBLE | T_PACKED, 1, 4); + } + } + + private static void legal(int conv, int types, int minSize, int maxSize) { + LEGAL_TYPES[conv] = types; + MIN_SIZE[conv] = minSize; + MAX_SIZE[conv] = maxSize; + } + + private static int typeBit(int type) { + if (type >= GL11.GL_BYTE && type <= GL30.GL_HALF_FLOAT) return 1 << type - GL11.GL_BYTE; + return switch (type) { + case GL41.GL_FIXED -> T_FIXED; + case GL12.GL_UNSIGNED_INT_2_10_10_10_REV, GL33.GL_INT_2_10_10_10_REV -> T_PACKED; + case GL30.GL_UNSIGNED_INT_10F_11F_11F_REV -> T_PACKED_FLOAT; + default -> 0; + }; + } + + private static boolean isLegal(int types, int minSize, int maxSize, boolean bgra, int size, int type) { + final int bit = typeBit(type); + if ((types & bit) == 0) return false; + if (size == GL12.GL_BGRA) return bgra && (bit & (T_UBYTE | T_PACKED)) != 0; + return size >= minSize && size <= maxSize; + } + + public static boolean isLegalConventional(int conv, int size, int type) { + return isLegal(LEGAL_TYPES[conv], MIN_SIZE[conv], MAX_SIZE[conv], conv == CONV_COLOR || conv == CONV_SECONDARY_COLOR, size, type); + } + + public static boolean isLegalGeneric(int index, int size, int type, boolean integer) { + return index >= 0 && index < MAX_ATTRIBS && isLegal(integer ? T_INTEGER : T_GENERIC, 1, 4, !integer, size, type); + } + private int currentVertexFlags = 0; + private int genericEnabled = 0; + private int conventionalEnabled = 0; private Attrib[] currentAttribs = null; + private Attrib[] currentGeneric = null; + private Attrib[] currentConventional = null; public int boundEBO = 0; private int clientSideEnabledCount = 0; + private int arrayVersion = 0; + private int versionClock = 0; + private int bufferEpoch = 0; + private final Int2IntOpenHashMap bufferDeletedAt = new Int2IntOpenHashMap(); + private void touch() { + arrayVersion = ++versionClock; + } + + private boolean isBufferLive(int buffer, int sinceEpoch) { + return bufferDeletedAt.get(buffer) <= sinceEpoch; + } + + public boolean isBufferLive(Attrib a) { + return isBufferLive(a.vboId, a.epoch); + } public void init(int defaultVAO) { final VAOData data = new VAOData(); @@ -45,6 +156,10 @@ public final class VAOManager { // Save old VAO data if (current != null) { current.vertexFlags = currentVertexFlags; + current.genericEnabled = genericEnabled; + current.conventionalEnabled = conventionalEnabled; + current.generic = currentGeneric; + current.conventional = currentConventional; if (current.ebo != boundEBO) { removeEboOwner(current.ebo, current); current.ebo = boundEBO; @@ -52,6 +167,7 @@ public final class VAOManager { } current.attribs = currentAttribs; current.clientSideEnabledCount = clientSideEnabledCount; + current.arrayVersion = arrayVersion; } VAOData data = vaoMap.get(vaoId); @@ -61,9 +177,14 @@ public final class VAOManager { } current = data; currentVertexFlags = data.vertexFlags; + genericEnabled = data.genericEnabled; + conventionalEnabled = data.conventionalEnabled; currentAttribs = data.attribs; + currentGeneric = data.generic; + currentConventional = data.conventional; boundEBO = data.ebo; clientSideEnabledCount = data.clientSideEnabledCount; + arrayVersion = data.arrayVersion; } public void onDeleteVertexArray(int vaoId) { @@ -73,8 +194,25 @@ public final class VAOManager { if (data == current) current = null; } + private static boolean detach(Attrib[] slots, int buffer) { + boolean detached = false; + for (final Attrib a : slots) { + if (a != null && a.vboId == buffer) { + a.vboId = 0; + detached = true; + } + } + return detached; + } + public void onDeleteBuffer(int buffer) { if (buffer == 0) return; + bufferDeletedAt.put(buffer, ++bufferEpoch); + if (currentAttribs != null) { + boolean detached = detach(currentGeneric != null ? currentGeneric : currentAttribs, buffer); + if (currentConventional != null) detached |= detach(currentConventional, buffer); + if (detached) touch(); + } VAOData owner = eboOwners.remove(buffer); while (owner != null) { final VAOData next = owner.nextEboOwner; @@ -112,14 +250,19 @@ public final class VAOManager { public void enableClientVertexFlag(int flag) { currentVertexFlags |= flag; } public void disableClientVertexFlag(int flag) { currentVertexFlags &= ~flag; } public int getVertexFlags() { return currentVertexFlags; } - public void setVertexFlags(int flags) { currentVertexFlags = flags; } + public boolean isConventionalEnabled(int conv) { return (conventionalEnabled >> conv & 1) != 0; } public static void setCurrentVertexFlags(int flags) { GLStateManager.ctx().vaos.currentVertexFlags = flags; } public static final class VAOData { public Attrib[] attribs; public int vertexFlags; + public int genericEnabled; + public int conventionalEnabled; + public Attrib[] generic; + public Attrib[] conventional; public int ebo; public int clientSideEnabledCount; + int arrayVersion; private VAOData nextEboOwner; public VAOData() { @@ -127,92 +270,307 @@ public final class VAOManager { } } - public void setAttribute(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) { - if (index < 0 || index >= MAX_ATTRIBS) return; - final Attrib a = getAttrib(index); - final boolean wasClient = a.clientPointer != null; + public boolean setAttribute(int index, int size, int type, boolean normalized, int stride, long offset, int vboId) { + return setGeneric(index, size, type, normalized, false, stride, offset, vboId, null); + } + + public boolean setAttribute(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) { + return setGeneric(index, size, type, normalized, false, stride, 0, 0, pointer); + } + + public boolean setIntegerAttribute(int index, int size, int type, int stride, long offset, int vboId) { + return setGeneric(index, size, type, false, true, stride, offset, vboId, null); + } + + public boolean setIntegerAttribute(int index, int size, int type, int stride, ByteBuffer pointer) { + return setGeneric(index, size, type, false, true, stride, 0, 0, pointer); + } + + public boolean setConventional(int conv, int size, int type, boolean normalized, int stride, long offset, int vboId) { + return setConventional(conv, size, type, normalized, stride, offset, vboId, null); + } + + public boolean setConventional(int conv, int size, int type, boolean normalized, int stride, ByteBuffer pointer) { + return setConventional(conv, size, type, normalized, stride, 0, 0, pointer); + } + + private boolean setGeneric(int index, int size, int type, boolean normalized, boolean integer, int stride, long offset, int vboId, ByteBuffer pointer) { + if (index < 0 || index >= MAX_ATTRIBS) return false; + final Attrib g = genericSlot(index); + store(g, size, type, normalized, integer, stride, offset, vboId, pointer); + if (currentAttribs[index] == g) return true; + if (!isGenericEnabled(index) && isLocationConventionalEnabled(index)) return false; + apply(index, g); + return true; + } + + private boolean setConventional(int conv, int size, int type, boolean normalized, int stride, long offset, int vboId, ByteBuffer pointer) { + if (conv < 0 || conv >= CONV_COUNT) return false; + final Attrib c = conventionalSlot(conv); + store(c, size, type, normalized, false, stride, offset, vboId, pointer); + final int index = CONV_LOCATION[conv]; + if (index < 0) return false; + if (currentAttribs[index] == c) return true; + if (isGenericEnabled(index)) return false; + apply(index, c); + return true; + } + + private void store(Attrib a, int size, int type, boolean normalized, boolean integer, int stride, long offset, int vboId, ByteBuffer pointer) { + final boolean wasClient = a.isClientSide(); a.size = size; a.type = type; a.normalized = normalized; + a.integer = integer; a.stride = stride; a.offset = offset; a.vboId = vboId; - a.clientPointer = null; - a.genericPointer = true; - if (wasClient && a.enabled) { - clientSideEnabledCount--; - } - } - - public void setAttribute(int index, int size, int type, boolean normalized, int stride, ByteBuffer pointer) { - if (index < 0 || index >= MAX_ATTRIBS) return; - final Attrib a = getAttrib(index); - final boolean wasClient = a.clientPointer != null; - a.size = size; - a.type = type; - a.normalized = normalized; - a.stride = stride; - a.offset = 0; - a.vboId = 0; + a.epoch = bufferEpoch; a.clientPointer = pointer; - a.genericPointer = true; - if (!wasClient && a.enabled) { - clientSideEnabledCount++; + if (wasClient != a.isClientSide()) clientSideEnabledCount += wasClient ? -1 : 1; + touch(); + } + + private void apply(int index, Attrib source) { + final Attrib old = currentAttribs[index]; + if (old != null) { + if (old.isClientSide()) clientSideEnabledCount--; + source.enabled = old.enabled; + old.enabled = false; + if (source.isClientSide()) clientSideEnabledCount++; + } + currentAttribs[index] = source; + } + + private boolean isGenericEnabled(int index) { + return (genericEnabled >> index & 1) != 0; + } + + private boolean isLocationConventionalEnabled(int index) { + final int conv = LOCATION_CONV[index]; + return conv >= 0 && isConventionalEnabled(conv); + } + + private Attrib generic(int index) { + return (currentGeneric != null ? currentGeneric : currentAttribs)[index]; + } + + private Attrib genericSlot(int index) { + final Attrib[] slots = currentGeneric != null ? currentGeneric : currentAttribs; + Attrib g = slots[index]; + if (g == null) { + g = new Attrib(); + g.genericPointer = true; + slots[index] = g; + if (currentAttribs[index] == null) currentAttribs[index] = g; + } + return g; + } + + private Attrib conventionalSlot(int conv) { + if (currentConventional == null) { + currentConventional = new Attrib[CONV_COUNT]; + currentGeneric = currentAttribs.clone(); + } + Attrib c = currentConventional[conv]; + if (c == null) { + c = new Attrib(); + currentConventional[conv] = c; + } + return c; + } + + private boolean refresh(int index) { + touch(); + boolean repoint = false; + final boolean conventional = isLocationConventionalEnabled(index); + final Attrib winner = isGenericEnabled(index) ? genericSlot(index) : conventional ? currentConventional[LOCATION_CONV[index]] : null; + if (winner != null && winner != currentAttribs[index]) { + apply(index, winner); + repoint = true; + } + final boolean enabled = winner != null; + Attrib e = currentAttribs[index]; + if (e == null) { + if (!enabled) return repoint; + e = genericSlot(index); + } + if (e.enabled != enabled) { + if (e.clientPointer != null) clientSideEnabledCount += enabled ? 1 : -1; + e.enabled = enabled; + } + return repoint; + } + + public boolean enableAttribute(int index) { + if (index < 0 || index >= MAX_ATTRIBS) return false; + genericEnabled |= 1 << index; + return refresh(index); + } + + public boolean disableAttribute(int index) { + if (index < 0 || index >= MAX_ATTRIBS) return false; + genericEnabled &= ~(1 << index); + return refresh(index); + } + + public boolean setConventionalEnabled(int conv, boolean enabled) { + if (conv < 0 || conv >= CONV_COUNT) return false; + conventionalSlot(conv); + if (enabled) { + conventionalEnabled |= 1 << conv; + } else { + conventionalEnabled &= ~(1 << conv); + } + final int index = CONV_LOCATION[conv]; + if (index >= 0) return refresh(index); + touch(); + return false; + } + + public boolean setDivisor(int index, int divisor) { + if (index < 0 || index >= MAX_ATTRIBS) return true; + final Attrib g = genericSlot(index); + g.divisor = divisor; + touch(); + return currentAttribs[index] == g; + } + + public @Nullable Attrib conventional(int conv) { + return conv >= 0 && currentConventional != null ? currentConventional[conv] : null; + } + + public static final class ClientArrays { + public int boundVAO; + public int boundVBO; + public int boundEBO; + public int clientActiveTextureUnit; + int arrayVersion; + int bufferEpoch; + int vertexFlags; + int genericEnabled; + int conventionalEnabled; + int genericPresent; + int conventionalPresent; + int effectiveGeneric; + final Attrib[] generic = new Attrib[MAX_ATTRIBS]; + final Attrib[] conventional = new Attrib[CONV_COUNT]; + } + + private static void saveSlot(Attrib[] saved, int i, Attrib source) { + if (saved[i] == null) saved[i] = new Attrib(); + saved[i].copyPointer(source); + } + + public boolean isBufferLive(int buffer, ClientArrays saved) { + return isBufferLive(buffer, saved.bufferEpoch); + } + + public void saveClientArrays(ClientArrays out) { + out.arrayVersion = arrayVersion; + out.bufferEpoch = bufferEpoch; + out.vertexFlags = currentVertexFlags; + out.genericEnabled = genericEnabled; + out.conventionalEnabled = conventionalEnabled; + out.genericPresent = 0; + out.conventionalPresent = 0; + out.effectiveGeneric = 0; + for (int i = 0; i < MAX_ATTRIBS; i++) { + final Attrib g = generic(i); + if (g != null) { + out.genericPresent |= 1 << i; + saveSlot(out.generic, i, g); + } + final Attrib e = currentAttribs[i]; + if (e != null && e.genericPointer) out.effectiveGeneric |= 1 << i; + } + if (currentConventional == null) return; + for (int i = 0; i < CONV_COUNT; i++) { + final Attrib c = currentConventional[i]; + if (c != null) { + out.conventionalPresent |= 1 << i; + saveSlot(out.conventional, i, c); + } } } - public boolean isGenericPointerEnabled(int index) { + private final Attrib[] restoreBefore = new Attrib[MAX_ATTRIBS]; + + public int restoreClientArrays(ClientArrays in) { + currentVertexFlags = in.vertexFlags; + if (arrayVersion == in.arrayVersion) return 0; + for (int i = 0; i < MAX_ATTRIBS; i++) { + if (restoreBefore[i] == null) restoreBefore[i] = new Attrib(); + final Attrib e = currentAttribs[i]; + if (e == null) { + restoreBefore[i].clearPointer(); + restoreBefore[i].enabled = false; + } else { + restoreBefore[i].copyPointer(e); + restoreBefore[i].enabled = e.enabled; + } + } + genericEnabled = in.genericEnabled; + conventionalEnabled = in.conventionalEnabled; + for (int i = 0; i < MAX_ATTRIBS; i++) { + if ((in.genericPresent >> i & 1) != 0) { + genericSlot(i).copyPointer(in.generic[i]); + } else { + final Attrib g = generic(i); + if (g != null) g.clearPointer(); + } + } + if (currentConventional != null || in.conventionalPresent != 0) { + for (int i = 0; i < CONV_COUNT; i++) { + if ((in.conventionalPresent >> i & 1) != 0) { + conventionalSlot(i).copyPointer(in.conventional[i]); + } else if (currentConventional != null && currentConventional[i] != null) { + currentConventional[i].clearPointer(); + } + } + } + clientSideEnabledCount = 0; + for (int i = 0; i < MAX_ATTRIBS; i++) { + final int conv = LOCATION_CONV[i]; + final Attrib g = generic(i); + final Attrib c = conventional(conv); + final boolean conventional = isLocationConventionalEnabled(i); + Attrib e; + if (isGenericEnabled(i)) { + e = genericSlot(i); + } else if (conventional) { + e = conventionalSlot(conv); + } else { + e = c == null || (in.effectiveGeneric >> i & 1) != 0 ? g : c; + } + if (g != null) g.enabled = false; + if (c != null) c.enabled = false; + currentAttribs[i] = e; + if (e != null) { + e.enabled = isGenericEnabled(i) || conventional; + if (e.isClientSide()) clientSideEnabledCount++; + } + } + int changed = 0; + for (int i = 0; i < MAX_ATTRIBS; i++) { + final Attrib e = currentAttribs[i]; + final Attrib before = restoreBefore[i]; + if (e == null) { + if (before.enabled) changed |= 0x10000 << i; + } else { + if (e.enabled != before.enabled) changed |= 0x10000 << i; + if (!e.samePointer(before)) changed |= 1 << i; + } + before.clientPointer = null; + } + arrayVersion = in.arrayVersion; + return changed; + } + + public boolean isAttribEnabled(int index) { if (index < 0 || index >= MAX_ATTRIBS) return false; final Attrib a = currentAttribs[index]; - return a != null && a.genericPointer && a.enabled; - } - - public void markConventional(int index) { - if (index < 0 || index >= MAX_ATTRIBS) return; - final Attrib a = currentAttribs[index]; - if (a != null) a.genericPointer = false; - } - - public void setEnabled(int index, boolean enabled) { - if (index < 0 || index >= MAX_ATTRIBS) return; - final Attrib a = getAttrib(index); - if (a.clientPointer != null && a.enabled != enabled) { - clientSideEnabledCount += enabled ? 1 : -1; - } - a.enabled = enabled; - } - - public void enableAttribute(int index) { - if (index < 0 || index >= MAX_ATTRIBS) return; - final Attrib a = getAttrib(index); - if (a.clientPointer != null && !a.enabled) { - clientSideEnabledCount++; - } - a.enabled = true; - } - - public void disableAttribute(int index) { - if (index < 0 || index >= MAX_ATTRIBS) return; - final Attrib a = getAttrib(index); - if (a.clientPointer != null && a.enabled) { - clientSideEnabledCount--; - } - a.enabled = false; - } - - // Lazily allocate each attrib. Most VAOs don't even have 4 attribs, let alone 16. - private Attrib getAttrib(int index) { - Attrib a = currentAttribs[index]; - if (a == null) { - a = new Attrib(); - currentAttribs[index] = a; - } - return a; - } - - public boolean isClientSideAttrib(int index) { - final Attrib attrib = currentAttribs[index]; - return attrib != null && attrib.isClientSide(); + return a != null && a.enabled; } public Attrib attrib(int index) { @@ -286,7 +644,11 @@ public final class VAOManager { if (clientArraysVBOOffsets[i] < 0) continue; final VAOManager.Attrib a = currentAttribs[i]; RENDER_BACKEND.bufferSubData(GL15.GL_ARRAY_BUFFER, clientArraysVBOOffsets[i], a.clientPointer.duplicate()); - RENDER_BACKEND.vertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (long) clientArraysVBOOffsets[i]); + if (a.integer) { + RENDER_BACKEND.vertexAttribIPointer(i, a.size, a.type, a.stride, (long) clientArraysVBOOffsets[i]); + } else { + RENDER_BACKEND.vertexAttribPointer(i, a.size, a.type, a.normalized, a.stride, (long) clientArraysVBOOffsets[i]); + } } GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, savedVBO); @@ -298,9 +660,12 @@ public final class VAOManager { public int size; public int type; public boolean normalized; + public boolean integer; public int stride; public long offset; public int vboId; + public int divisor; + int epoch; public ByteBuffer clientPointer; public boolean genericPointer; @@ -308,16 +673,35 @@ public final class VAOManager { return enabled && clientPointer != null; } - public void reset() { - enabled = false; + void copyPointer(Attrib source) { + size = source.size; + type = source.type; + normalized = source.normalized; + integer = source.integer; + stride = source.stride; + offset = source.offset; + vboId = source.vboId; + divisor = source.divisor; + epoch = source.epoch; + clientPointer = source.clientPointer; + } + + boolean samePointer(Attrib other) { + return size == other.size && type == other.type && normalized == other.normalized && integer == other.integer + && stride == other.stride && offset == other.offset && vboId == other.vboId && divisor == other.divisor + && clientPointer == other.clientPointer; + } + + void clearPointer() { size = 0; type = 0; normalized = false; + integer = false; stride = 0; offset = 0; vboId = 0; + divisor = 0; clientPointer = null; - genericPointer = false; } public int effectiveStride() { diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirector.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirector.java index f06cb6ccf..a7574f3cd 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirector.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirector.java @@ -143,6 +143,7 @@ public class GLSMRedirector { private static final String OpenGlHelper = "net/minecraft/client/renderer/OpenGlHelper"; private static final String EXTBlendFunc = "org/lwjgl/opengl/EXTBlendFuncSeparate"; private static final String EXTSecondaryColor = "org/lwjgl/opengl/EXTSecondaryColor"; + private static final String EXTFogCoord = "org/lwjgl/opengl/EXTFogCoord"; private static final String ARBMultiTexture = "org/lwjgl/opengl/ARBMultitexture"; private static final String ARBShaderObjects = "org/lwjgl/opengl/ARBShaderObjects"; private static final String ARBInstancedArrays = "org/lwjgl/opengl/ARBInstancedArrays"; @@ -378,6 +379,8 @@ public class GLSMRedirector { .add("glEnableClientState") .add("glDisableClientState") .add("glInterleavedArrays") + .add("glEdgeFlagPointer") + .add("glGetPointer") .add("glPushClientAttrib") .add("glPopClientAttrib") .add("glViewport") @@ -408,11 +411,16 @@ public class GLSMRedirector { .add("glMultiDrawArrays") .add("glPointParameterf") .add("glPointParameteri") + .add("glPointParameter") + .add("glPointParameterfv", "glPointParameter") + .add("glPointParameteriv", "glPointParameter") // Removed in core profile - emulated by the FFP shaders, calling through would abort the JVM .add("glSecondaryColor3f") .add("glSecondaryColor3d") .add("glSecondaryColor3b") - .add("glSecondaryColor3ub"); + .add("glSecondaryColor3ub") + .add("glSecondaryColorPointer") + .add("glFogCoordPointer"); final var gl15 = RedirectMap.newMap() .add("glGenBuffers") .add("glBindBuffer") @@ -604,7 +612,9 @@ public class GLSMRedirector { .add("glSecondaryColor3bEXT", "glSecondaryColor3b") .add("glSecondaryColor3fEXT", "glSecondaryColor3f") .add("glSecondaryColor3dEXT", "glSecondaryColor3d") - .add("glSecondaryColor3ubEXT", "glSecondaryColor3ub")); + .add("glSecondaryColor3ubEXT", "glSecondaryColor3ub") + .add("glSecondaryColorPointerEXT", "glSecondaryColorPointer")); + methodRedirects.put(EXTFogCoord, RedirectMap.newMap().add("glFogCoordPointerEXT", "glFogCoordPointer")); // ARB methodRedirects.put(ARBMultiTexture, RedirectMap.newMap() diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/PointState.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/PointState.java index e6a31693f..fb58358e5 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/PointState.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/PointState.java @@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.glsm.states; import lombok.Getter; import lombok.Setter; +import org.lwjgl.opengl.GL20; /** * Tracks GL point state: size. @@ -10,10 +11,24 @@ import lombok.Setter; @Getter @Setter public class PointState implements ISettableState { private float size = 1.0f; + private float minSize = 0.0f; + private float maxSize = Float.NaN; + private float fadeThreshold = 1.0f; + private float attenuationConstant = 1.0f; + private float attenuationLinear = 0.0f; + private float attenuationQuadratic = 0.0f; + private int spriteCoordOrigin = GL20.GL_UPPER_LEFT; @Override public PointState set(PointState state) { this.size = state.size; + this.minSize = state.minSize; + this.maxSize = state.maxSize; + this.fadeThreshold = state.fadeThreshold; + this.attenuationConstant = state.attenuationConstant; + this.attenuationLinear = state.attenuationLinear; + this.attenuationQuadratic = state.attenuationQuadratic; + this.spriteCoordOrigin = state.spriteCoordOrigin; return this; } @@ -21,7 +36,14 @@ public class PointState implements ISettableState { public boolean sameAs(Object state) { if (this == state) return true; if (!(state instanceof PointState pointState)) return false; - return Float.compare(size, pointState.size) == 0; + return Float.compare(size, pointState.size) == 0 + && Float.compare(minSize, pointState.minSize) == 0 + && Float.compare(maxSize, pointState.maxSize) == 0 + && Float.compare(fadeThreshold, pointState.fadeThreshold) == 0 + && Float.compare(attenuationConstant, pointState.attenuationConstant) == 0 + && Float.compare(attenuationLinear, pointState.attenuationLinear) == 0 + && Float.compare(attenuationQuadratic, pointState.attenuationQuadratic) == 0 + && spriteCoordOrigin == pointState.spriteCoordOrigin; } @Override diff --git a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/TextureUnitArray.java b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/TextureUnitArray.java index e3b856ef6..0bbbf8a89 100644 --- a/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/TextureUnitArray.java +++ b/glsm/src/main/java/com/gtnewhorizons/angelica/glsm/states/TextureUnitArray.java @@ -9,6 +9,7 @@ import lombok.Getter; import org.joml.Matrix4fStack; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; +import org.lwjgl.opengl.GL13; import java.util.ArrayList; import java.util.List; @@ -19,6 +20,7 @@ public class TextureUnitArray { private final TextureUnitBooleanStateStack[] states; private final TextureUnitBooleanStateStack[] texture1DStates; private final TextureUnitBooleanStateStack[] texture3DStates; + private final TextureUnitBooleanStateStack[] textureCubeMapStates; private final TextureUnitBooleanStateStack[] texGenSStates; private final TextureUnitBooleanStateStack[] texGenTStates; private final TextureUnitBooleanStateStack[] texGenRStates; @@ -36,6 +38,7 @@ public class TextureUnitArray { states = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS]; texture1DStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS]; texture3DStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS]; + textureCubeMapStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS]; texGenSStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS]; texGenTStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS]; texGenRStates = new TextureUnitBooleanStateStack[GLStateManager.MAX_TEXTURE_UNITS]; @@ -49,6 +52,7 @@ public class TextureUnitArray { states[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_2D, i)); texture1DStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_1D, i)); texture3DStates[i] = track(new TextureUnitBooleanStateStack(GL12.GL_TEXTURE_3D, i)); + textureCubeMapStates[i] = track(new TextureUnitBooleanStateStack(GL13.GL_TEXTURE_CUBE_MAP, i)); texGenSStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_S, i)); texGenTStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_T, i)); texGenRStates[i] = track(new TextureUnitBooleanStateStack(GL11.GL_TEXTURE_GEN_R, i)); @@ -90,6 +94,10 @@ public class TextureUnitArray { return texture3DStates[index]; } + public TextureUnitBooleanStateStack getTextureCubeMapStates(int index) { + return textureCubeMapStates[index]; + } + public TextureUnitBooleanStateStack getTexGenSStates(int index) { return texGenSStates[index]; } @@ -127,6 +135,7 @@ public class TextureUnitArray { if (!states[i].sameAs(textureUnitArray.states[i])) return false; if (!texture1DStates[i].sameAs(textureUnitArray.texture1DStates[i])) return false; if (!texture3DStates[i].sameAs(textureUnitArray.texture3DStates[i])) return false; + if (!textureCubeMapStates[i].sameAs(textureUnitArray.textureCubeMapStates[i])) return false; if (!texGenSStates[i].sameAs(textureUnitArray.texGenSStates[i])) return false; if (!texGenTStates[i].sameAs(textureUnitArray.texGenTStates[i])) return false; if (!texGenRStates[i].sameAs(textureUnitArray.texGenRStates[i])) return false; diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ArrayUniformUploadGLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ArrayUniformUploadGLTest.java index fc8e848d8..f6130ee01 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ArrayUniformUploadGLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ArrayUniformUploadGLTest.java @@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.glsm; import org.junit.jupiter.api.Test; import org.lwjgl.BufferUtils; -import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL20; import java.nio.FloatBuffer; @@ -14,29 +13,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @GLCoreTest class ArrayUniformUploadGLTest { - private static int compile(int type, String src) { - final int s = GL20.glCreateShader(type); - GL20.glShaderSource(s, src); - GL20.glCompileShader(s); - assertEquals(GL11.GL_TRUE, GL20.glGetShaderi(s, GL20.GL_COMPILE_STATUS), () -> "shader compile failed: " + GL20.glGetShaderInfoLog(s, 4096)); - return s; - } - - private static int link(String vs, String fs) { - final int v = compile(GL20.GL_VERTEX_SHADER, vs); - final int f = compile(GL20.GL_FRAGMENT_SHADER, fs); - final int p = GL20.glCreateProgram(); - GL20.glAttachShader(p, v); - GL20.glAttachShader(p, f); - GL20.glLinkProgram(p); - assertEquals(GL11.GL_TRUE, GL20.glGetProgrami(p, GL20.GL_LINK_STATUS), () -> "link failed: " + GL20.glGetProgramInfoLog(p, 4096)); - GL20.glDeleteShader(v); - GL20.glDeleteShader(f); - return p; - } - - private static final String VS = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }\n"; - @Test void glUniform2FloatBufferUploadsWholeArray() { // Runtime index keeps the whole array active so element locations stay queryable. @@ -45,7 +21,7 @@ class ArrayUniformUploadGLTest { + "uniform int idx;\n" + "out vec4 o;\n" + "void main(){ o = vec4(cosmicuvs[idx], cosmicuvs[(idx+1)%10]); }\n"; - final int p = link(VS, fs); + final int p = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, fs); try { GL20.glUseProgram(p); @@ -66,7 +42,7 @@ class ArrayUniformUploadGLTest { } } finally { GL20.glUseProgram(0); - GL20.glDeleteProgram(p); + GLStateManager.glDeleteProgram(p); } } @@ -77,7 +53,7 @@ class ArrayUniformUploadGLTest { + "uniform int idx;\n" + "out vec4 o;\n" + "void main(){ o = vec4(float(vals[idx])); }\n"; - final int p = link(VS, fs); + final int p = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, fs); try { GL20.glUseProgram(p); @@ -96,7 +72,7 @@ class ArrayUniformUploadGLTest { } } finally { GL20.glUseProgram(0); - GL20.glDeleteProgram(p); + GLStateManager.glDeleteProgram(p); } } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/CaptureGateTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/CaptureGateTest.java index 2b29699de..4458cdc1a 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/CaptureGateTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/CaptureGateTest.java @@ -5,30 +5,24 @@ import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class CaptureGateTest { - @Test - void gateOffWhenNothingAttached() { - assertFalse(CaptureGate.gateValue(false, false, false)); - } - - @Test - void gateOnWhenForced() { - assertTrue(CaptureGate.gateValue(true, false, false)); - } - - @Test - void gateOnWhenToolAttached() { - assertTrue(CaptureGate.gateValue(false, true, false)); - } - - @Test - void gateOnWhenTracyConnected() { - assertTrue(CaptureGate.gateValue(false, false, true)); + @ParameterizedTest(name = "{0}") + @CsvSource({ + "nothing attached, false, false, false, false", + "forced, true, false, false, true", + "tool attached, false, true, false, true", + "tracy connected, false, false, true, true" + }) + void gateIsOnWhenAnySourceIs(String name, boolean forced, boolean toolAttached, boolean tracyConnected, boolean expected) { + assertEquals(expected, CaptureGate.gateValue(forced, toolAttached, tracyConnected)); } @Test diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/CompatShaderTransformerTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/CompatShaderTransformerTest.java index 0ff1deaba..68a2bcd46 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/CompatShaderTransformerTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/CompatShaderTransformerTest.java @@ -5,9 +5,12 @@ import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import java.nio.file.Path; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -17,66 +20,104 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class CompatShaderTransformerTest { - @Test - void testModelViewMatrixReplacement() { - String src = """ - #version 120 - void main() { - vec4 pos = gl_ModelViewMatrix * gl_Vertex; - gl_Position = gl_ProjectionMatrix * pos; - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertTrue(result.contains("angelica_ModelViewMatrix")); - assertTrue(result.contains("angelica_ProjectionMatrix")); - assertFalse(result.contains("gl_ModelViewMatrix")); - assertFalse(result.contains("gl_ProjectionMatrix")); - assertTrue(result.contains("#version 330 core")); + private static String[] strings(String... values) { + return values; } - @Test - void testNormalMatrixReplacement() { - String src = """ - #version 120 - void main() { - vec3 n = gl_NormalMatrix * gl_Normal; - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertTrue(result.contains("uniform mat3 angelica_NormalMatrix")); - assertFalse(result.contains("gl_NormalMatrix")); + private static void assertRewrite(boolean fragment, String src, String[] present, String[] absent) { + final String result = CompatShaderTransformer.transform(src, fragment); + for (final String s : present) { + assertTrue(result.contains(s), () -> "expected '" + s + "' in\n\n" + result); + } + for (final String s : absent) { + assertFalse(result.contains(s), () -> "'" + s + "' must not remain in\n\n" + result); + } } - @Test - void testModelViewProjectionMatrixExpression() { - String src = """ - #version 120 - void main() { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertFalse(result.contains("gl_ModelViewProjectionMatrix")); - assertTrue(result.contains("angelica_ProjectionMatrix")); + static Stream matrixBuiltins() { + return Stream.of( + Arguments.of("modelview and projection matrices", false, """ + #version 120 + void main() { + vec4 pos = gl_ModelViewMatrix * gl_Vertex; + gl_Position = gl_ProjectionMatrix * pos; + } + """, strings("angelica_ModelViewMatrix", "angelica_ProjectionMatrix", "#version 330 core"), strings("gl_ModelViewMatrix", "gl_ProjectionMatrix")), + Arguments.of("normal matrix", false, """ + #version 120 + void main() { + vec3 n = gl_NormalMatrix * gl_Normal; + } + """, strings("uniform mat3 angelica_NormalMatrix"), strings("gl_NormalMatrix")), + Arguments.of("modelview-projection matrix", false, """ + #version 120 + void main() { + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + } + """, strings("angelica_ProjectionMatrix"), strings("gl_ModelViewProjectionMatrix")), + Arguments.of("inverse matrices", false, """ + #version 120 + void main() { + vec4 pos = gl_ModelViewMatrixInverse * vec4(0.0, 0.0, 0.0, 1.0); + vec4 proj = gl_ProjectionMatrixInverse * vec4(0.0, 0.0, -1.0, 1.0); + } + """, strings(), strings("gl_ModelViewMatrixInverse", "gl_ProjectionMatrixInverse")), + Arguments.of("texture matrix", false, """ + #version 120 + void main() { + vec4 lmCoord = gl_TextureMatrix[1] * gl_MultiTexCoord1; + } + """, strings(), strings("gl_TextureMatrix")), + Arguments.of("several builtins in one fragment shader", true, """ + #version 120 + uniform sampler2D tex; + void main() { + vec3 n = gl_NormalMatrix * gl_Normal; + vec4 pos = gl_ModelViewMatrix * gl_Vertex; + gl_Position = gl_ProjectionMatrix * pos; + vec4 color = texture2D(tex, vec2(0.0)); + gl_FragColor = color; + } + """, strings("angelica_ModelViewMatrix", "angelica_ProjectionMatrix", "angelica_NormalMatrix", "#version 330 core"), strings("gl_ModelViewMatrix", "gl_ProjectionMatrix", "gl_NormalMatrix", "texture2D", "gl_FragColor"))); } - @Test - void testTextureFunctionRenames() { - String src = """ - #version 120 - uniform sampler2D tex; - void main() { - vec4 color = texture2D(tex, vec2(0.0)); - } - """; + @ParameterizedTest(name = "{0}") + @MethodSource("matrixBuiltins") + void matrixBuiltinsAreRewritten(String name, boolean fragment, String src, String[] present, String[] absent) { + assertRewrite(fragment, src, present, absent); + } - String result = CompatShaderTransformer.transform(src, false); - // texture2D -> texture - assertFalse(result.contains("texture2D")); - assertTrue(result.contains("texture")); + static Stream textureFunctions() { + return Stream.of( + Arguments.of("texture2D", false, """ + #version 120 + uniform sampler2D tex; + void main() { + vec4 color = texture2D(tex, vec2(0.0)); + } + """, strings("texture"), strings("texture2D")), + Arguments.of("shadow2D", true, """ + #version 120 + uniform sampler2DShadow shadowMap; + void main() { + float s = shadow2D(shadowMap, vec3(0.0)).r; + gl_FragColor = vec4(s); + } + """, strings("texture"), strings("shadow2D(")), + Arguments.of("texelFetch3D", true, """ + #version 120 + uniform sampler3D vol; + void main() { + vec4 v = texelFetch3D(vol, ivec3(0), 0); + gl_FragColor = v; + } + """, strings("texelFetch"), strings("texelFetch3D"))); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("textureFunctions") + void legacyTextureFunctionsAreRenamed(String name, boolean fragment, String src, String[] present, String[] absent) { + assertRewrite(fragment, src, present, absent); } @Test @@ -95,32 +136,26 @@ class CompatShaderTransformerTest { assertTrue(result.contains("layout")); } - @Test - void testVersionUpgradeTo330() { - String src = """ - #version 110 - void main() { - gl_FragColor = vec4(1.0); - } - """; - - String result = CompatShaderTransformer.transform(src, true); - assertTrue(result.contains("#version 330 core")); - assertFalse(result.contains("#version 110")); + static Stream versionDirectives() { + return Stream.of( + Arguments.of("110 becomes 330 core", true, """ + #version 110 + void main() { + gl_FragColor = vec4(1.0); + } + """, strings("#version 330 core"), strings("#version 110")), + Arguments.of("compatibility profile is stripped", false, """ + #version 330 compatibility + void main() { + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + } + """, strings("#version 330 core"), strings("compatibility"))); } - @Test - void testCompatibilityProfileStripped() { - String src = """ - #version 330 compatibility - void main() { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertTrue(result.contains("#version 330 core")); - assertFalse(result.contains("compatibility")); + @ParameterizedTest(name = "{0}") + @MethodSource("versionDirectives") + void versionIsUpgradedToCore(String name, boolean fragment, String src, String[] present, String[] absent) { + assertRewrite(fragment, src, present, absent); } @Test @@ -139,60 +174,6 @@ class CompatShaderTransformerTest { assertEquals(src, result); } - @Test - void testCombinedBuiltinsInSingleShader() { - String src = """ - #version 120 - uniform sampler2D tex; - void main() { - vec3 n = gl_NormalMatrix * gl_Normal; - vec4 pos = gl_ModelViewMatrix * gl_Vertex; - gl_Position = gl_ProjectionMatrix * pos; - vec4 color = texture2D(tex, vec2(0.0)); - gl_FragColor = color; - } - """; - - String result = CompatShaderTransformer.transform(src, true); - assertTrue(result.contains("angelica_ModelViewMatrix")); - assertTrue(result.contains("angelica_ProjectionMatrix")); - assertTrue(result.contains("angelica_NormalMatrix")); - assertFalse(result.contains("gl_ModelViewMatrix")); - assertFalse(result.contains("gl_ProjectionMatrix")); - assertFalse(result.contains("gl_NormalMatrix")); - assertFalse(result.contains("texture2D")); - assertFalse(result.contains("gl_FragColor")); - assertTrue(result.contains("#version 330 core")); - } - - @Test - void testInverseMatrixReplacement() { - String src = """ - #version 120 - void main() { - vec4 pos = gl_ModelViewMatrixInverse * vec4(0.0, 0.0, 0.0, 1.0); - vec4 proj = gl_ProjectionMatrixInverse * vec4(0.0, 0.0, -1.0, 1.0); - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertFalse(result.contains("gl_ModelViewMatrixInverse")); - assertFalse(result.contains("gl_ProjectionMatrixInverse")); - } - - @Test - void testTextureMatrixReplacement() { - String src = """ - #version 120 - void main() { - vec4 lmCoord = gl_TextureMatrix[1] * gl_MultiTexCoord1; - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertFalse(result.contains("gl_TextureMatrix")); - } - @Test void testFogStructAccess() { String src = """ @@ -211,34 +192,27 @@ class CompatShaderTransformerTest { assertTrue(result.contains("uniform vec4 angelica_FogColor"), "Fog color uniform should be injected"); } - @Test - void testFogFragCoordVertex() { - String src = """ - #version 120 - void main() { - gl_FogFragCoord = length(gl_ModelViewMatrix * gl_Vertex); - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertTrue(result.contains("out float angelica_FogFragCoord"), "Vertex shader should get 'out' declaration"); - assertTrue(result.contains("angelica_FogFragCoord"), "gl_FogFragCoord should be renamed"); - assertFalse(result.contains("gl_FogFragCoord"), "gl_FogFragCoord should not remain"); + static Stream fogFragCoord() { + return Stream.of( + Arguments.of("vertex shader declares an out", false, """ + #version 120 + void main() { + gl_FogFragCoord = length(gl_ModelViewMatrix * gl_Vertex); + } + """, strings("out float angelica_FogFragCoord"), strings("gl_FogFragCoord")), + Arguments.of("fragment shader declares an in", true, """ + #version 120 + void main() { + float f = gl_FogFragCoord; + gl_FragColor = vec4(f); + } + """, strings("in float angelica_FogFragCoord"), strings("gl_FogFragCoord"))); } - @Test - void testFogFragCoordFragment() { - String src = """ - #version 120 - void main() { - float f = gl_FogFragCoord; - gl_FragColor = vec4(f); - } - """; - - String result = CompatShaderTransformer.transform(src, true); - assertTrue(result.contains("in float angelica_FogFragCoord"), "Fragment shader should get 'in' declaration"); - assertFalse(result.contains("gl_FogFragCoord"), "gl_FogFragCoord should not remain"); + @ParameterizedTest(name = "{0}") + @MethodSource("fogFragCoord") + void fogFragCoordBecomesAVarying(String name, boolean fragment, String src, String[] present, String[] absent) { + assertRewrite(fragment, src, present, absent); } @Test @@ -255,23 +229,6 @@ class CompatShaderTransformerTest { assertFalse(result.contains("gl_FrontColor"), "gl_FrontColor should not remain"); } - @Test - void testShadow2DRename() { - String src = """ - #version 120 - uniform sampler2DShadow shadowMap; - void main() { - float s = shadow2D(shadowMap, vec3(0.0)).r; - gl_FragColor = vec4(s); - } - """; - - String result = CompatShaderTransformer.transform(src, true); - assertFalse(result.contains("shadow2D("), "shadow2D should be renamed"); - // shadow2D -> texture with vec4 wrapping - assertTrue(result.contains("texture"), "shadow2D should be renamed to texture"); - } - @Test void testReservedWordTextureAsVariable() { // Shader declares 'texture' as a sampler variable - this is valid in GLSL 120 @@ -321,155 +278,81 @@ class CompatShaderTransformerTest { "Alpha test discard should be appended to main()"); } - @Test - void testTexelFetch3DTriggersTransformation() { - String src = """ - #version 120 - uniform sampler3D vol; - void main() { - vec4 v = texelFetch3D(vol, ivec3(0), 0); - gl_FragColor = v; - } - """; - - String result = CompatShaderTransformer.transform(src, true); - // texelFetch3D should be renamed to texelFetch - assertFalse(result.contains("texelFetch3D"), "texelFetch3D should be renamed"); - assertTrue(result.contains("texelFetch"), "texelFetch3D should become texelFetch"); + static Stream vertexAttributes() { + return Stream.of( + Arguments.of("gl_Vertex at location 0", false, """ + #version 120 + void main() { + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + } + """, strings("in vec4 angelica_Vertex", "location = 0"), strings("gl_Vertex")), + Arguments.of("gl_Color at location 1", false, """ + #version 120 + varying vec4 v_Color; + void main() { + v_Color = gl_Color; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + } + """, strings("in vec4 angelica_Color", "location = 1"), strings("gl_Color")), + Arguments.of("gl_MultiTexCoord0 at location 2", false, """ + #version 120 + void main() { + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + } + """, strings("in vec4 angelica_MultiTexCoord0", "location = 2"), strings("gl_MultiTexCoord0")), + Arguments.of("gl_MultiTexCoord1 at location 3", false, """ + #version 120 + void main() { + vec4 lm = gl_TextureMatrix[1] * gl_MultiTexCoord1; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + } + """, strings("angelica_MultiTexCoord1", "location = 3"), strings("gl_MultiTexCoord1")), + Arguments.of("gl_Normal at location 4", false, """ + #version 120 + void main() { + vec3 n = gl_NormalMatrix * gl_Normal; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + } + """, strings("in vec3 angelica_Normal", "location = 4"), strings("gl_Normal"))); } - @Test - void testVertexAttributeGlVertex() { - String src = """ - #version 120 - void main() { - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertFalse(result.contains("gl_Vertex"), "gl_Vertex should be replaced"); - assertTrue(result.contains("angelica_Vertex"), "should become angelica_Vertex"); - assertTrue(result.contains("in vec4 angelica_Vertex"), "should have in declaration"); - assertTrue(result.contains("location = 0"), "should use attribute location 0"); + @ParameterizedTest(name = "{0}") + @MethodSource("vertexAttributes") + void vertexAttributeBuiltinsGetFixedLocations(String name, boolean fragment, String src, String[] present, String[] absent) { + assertRewrite(fragment, src, present, absent); } - @Test - void testVertexAttributeGlColorInVertexShader() { - // gl_Color in a vertex shader is the per-vertex color attribute, not the fragment varying - String src = """ - #version 120 - varying vec4 v_Color; - void main() { - v_Color = gl_Color; - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertTrue(result.contains("angelica_Color"), "gl_Color -> angelica_Color (vertex attribute)"); - assertTrue(result.contains("in vec4 angelica_Color"), "should have in declaration"); - assertTrue(result.contains("location = 1"), "should use attribute location 1"); - assertFalse(result.contains("gl_Color"), "gl_Color should not remain"); + static Stream texCoordVaryings() { + return Stream.of( + Arguments.of("vertex shader declares an out", false, """ + #version 120 + void main() { + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + } + """, strings("out vec4 angelica_TexCoord0"), strings("gl_TexCoord")), + Arguments.of("fragment shader declares an in", true, """ + #version 120 + void main() { + vec2 uv = gl_TexCoord[0].xy; + gl_FragColor = vec4(uv, 0.0, 1.0); + } + """, strings("in vec4 angelica_TexCoord0"), strings("gl_TexCoord")), + Arguments.of("one varying per index", false, """ + #version 120 + void main() { + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1] = gl_MultiTexCoord1; + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + } + """, strings("out vec4 angelica_TexCoord0", "out vec4 angelica_TexCoord1"), strings("gl_TexCoord"))); } - @Test - void testVertexAttributeMultiTexCoord0() { - String src = """ - #version 120 - void main() { - gl_TexCoord[0] = gl_MultiTexCoord0; - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertFalse(result.contains("gl_MultiTexCoord0"), "gl_MultiTexCoord0 should be replaced"); - assertTrue(result.contains("angelica_MultiTexCoord0"), "should become angelica_MultiTexCoord0"); - assertTrue(result.contains("in vec4 angelica_MultiTexCoord0"), "should have in declaration"); - assertTrue(result.contains("location = 2"), "should use attribute location 2"); - } - - @Test - void testVertexAttributeMultiTexCoord1() { - String src = """ - #version 120 - void main() { - vec4 lm = gl_TextureMatrix[1] * gl_MultiTexCoord1; - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertFalse(result.contains("gl_MultiTexCoord1"), "gl_MultiTexCoord1 should be replaced"); - assertTrue(result.contains("angelica_MultiTexCoord1"), "should become angelica_MultiTexCoord1"); - assertTrue(result.contains("location = 3"), "should use attribute location 3"); - } - - @Test - void testVertexAttributeGlNormal() { - String src = """ - #version 120 - void main() { - vec3 n = gl_NormalMatrix * gl_Normal; - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertFalse(result.contains("gl_Normal"), "gl_Normal should be replaced"); - assertTrue(result.contains("angelica_Normal"), "should become angelica_Normal"); - assertTrue(result.contains("in vec3 angelica_Normal"), "should have in vec3 declaration"); - assertTrue(result.contains("location = 4"), "should use attribute location 4"); - } - - @Test - void testTexCoordVaryingVertexShader() { - String src = """ - #version 120 - void main() { - gl_TexCoord[0] = gl_MultiTexCoord0; - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertFalse(result.contains("gl_TexCoord"), "gl_TexCoord should be replaced"); - assertTrue(result.contains("angelica_TexCoord0"), "should become angelica_TexCoord0"); - assertTrue(result.contains("out vec4 angelica_TexCoord0"), "vertex shader should have 'out' declaration"); - } - - @Test - void testTexCoordVaryingFragmentShader() { - String src = """ - #version 120 - void main() { - vec2 uv = gl_TexCoord[0].xy; - gl_FragColor = vec4(uv, 0.0, 1.0); - } - """; - - String result = CompatShaderTransformer.transform(src, true); - assertFalse(result.contains("gl_TexCoord"), "gl_TexCoord should be replaced"); - assertTrue(result.contains("angelica_TexCoord0"), "should become angelica_TexCoord0"); - assertTrue(result.contains("in vec4 angelica_TexCoord0"), "fragment shader should have 'in' declaration"); - } - - @Test - void testTexCoordMultipleIndices() { - String src = """ - #version 120 - void main() { - gl_TexCoord[0] = gl_MultiTexCoord0; - gl_TexCoord[1] = gl_MultiTexCoord1; - gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; - } - """; - - String result = CompatShaderTransformer.transform(src, false); - assertTrue(result.contains("out vec4 angelica_TexCoord0"), "should have TexCoord0 out"); - assertTrue(result.contains("out vec4 angelica_TexCoord1"), "should have TexCoord1 out"); - assertFalse(result.contains("gl_TexCoord"), "no gl_TexCoord should remain"); + @ParameterizedTest(name = "{0}") + @MethodSource("texCoordVaryings") + void texCoordBecomesPerIndexVaryings(String name, boolean fragment, String src, String[] present, String[] absent) { + assertRewrite(fragment, src, present, absent); } @Test @@ -533,7 +416,6 @@ class CompatShaderTransformerTest { String result = CompatShaderTransformer.transform(src, false); assertFalse(result.contains("gl_FrontLightModelProduct"), "gl_FrontLightModelProduct should be replaced"); assertTrue(result.contains("uniform vec4 angelica_SceneColor"), "angelica_SceneColor uniform should be injected"); - assertTrue(result.contains("angelica_SceneColor"), "should reference angelica_SceneColor"); } @Test diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListCompilationParityTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListCompilationParityTest.java index f31a3738b..33fa51c92 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListCompilationParityTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListCompilationParityTest.java @@ -9,6 +9,7 @@ import org.lwjgl.opengl.GL11; import java.nio.FloatBuffer; +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.assertMatrixEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -64,12 +65,6 @@ class DisplayListCompilationParityTest { return buf; } - private static void assertMatrixEquals(FloatBuffer expected, FloatBuffer actual, String message) { - for (int i = 0; i < 16; i++) { - assertEquals(expected.get(i), actual.get(i), 0.001f, message + " (element " + i + ")"); - } - } - @BeforeEach void setup() { if (DisplayListManager.isRecording()) { diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListCompilationTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListCompilationTest.java index a02055a0e..ebdd08eab 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListCompilationTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListCompilationTest.java @@ -3,11 +3,9 @@ package com.gtnewhorizons.angelica.glsm; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; -import java.nio.FloatBuffer; - +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.matrix; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -48,12 +46,6 @@ class DisplayListCompilationTest { GLStateManager.glLoadIdentity(); } - private static FloatBuffer matrix(int pname) { - final FloatBuffer buf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(pname, buf); - return buf; - } - @Test void glsm_compileAndExecute_matrixModeWithTransforms() { GLStateManager.glMatrixMode(GL11.GL_PROJECTION); @@ -102,69 +94,4 @@ class DisplayListCompilationTest { assertFalse(DisplayListManager.isRecording(), "frame-begin backstop must clear a leaked open list"); } - - @Test - void rawGL_compile_immediateModeWithTransforms() { - GL11.glLoadIdentity(); - GL11.glTranslatef(100.0f, 0.0f, 0.0f); - - testList = GL11.glGenLists(1); - GL11.glNewList(testList, GL11.GL_COMPILE); - - GL11.glPushMatrix(); - GL11.glTranslatef(50.0f, 25.0f, 0.0f); - GL11.glScalef(0.5f, 0.5f, 1.0f); - - GL11.glBegin(GL11.GL_QUADS); - GL11.glVertex3f(0.0f, 0.0f, 0.0f); - GL11.glVertex3f(16.0f, 0.0f, 0.0f); - GL11.glVertex3f(16.0f, 16.0f, 0.0f); - GL11.glVertex3f(0.0f, 16.0f, 0.0f); - GL11.glEnd(); - - GL11.glPopMatrix(); - GL11.glEndList(); - - final FloatBuffer afterCompile = BufferUtils.createFloatBuffer(16); - GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, afterCompile); - assertEquals(100.0f, afterCompile.get(12), 0.001f, "After GL_COMPILE: matrix unchanged"); - - GL11.glCallList(testList); - - final FloatBuffer afterPlayback = BufferUtils.createFloatBuffer(16); - GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, afterPlayback); - assertEquals(100.0f, afterPlayback.get(12), 0.001f, "After playback: base position preserved"); - assertEquals(1.0f, afterPlayback.get(0), 0.001f, "After playback: no scale (popped)"); - - GL11.glLoadIdentity(); - } - - @Test - void rawGL_compileAndExecute_immediateModeExecutesImmediately() { - GL11.glLoadIdentity(); - - testList = GL11.glGenLists(1); - GL11.glNewList(testList, GL11.GL_COMPILE_AND_EXECUTE); - - GL11.glScalef(0.5f, 0.5f, 0.5f); - - final FloatBuffer during = BufferUtils.createFloatBuffer(16); - GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, during); - assertEquals(0.5f, during.get(0), 0.001f, "During COMPILE_AND_EXECUTE: scale applied"); - - GL11.glBegin(GL11.GL_QUADS); - GL11.glVertex3f(0.0f, 0.0f, 0.0f); - GL11.glVertex3f(1.0f, 0.0f, 0.0f); - GL11.glVertex3f(1.0f, 1.0f, 0.0f); - GL11.glVertex3f(0.0f, 1.0f, 0.0f); - GL11.glEnd(); - - GL11.glEndList(); - - final FloatBuffer after = BufferUtils.createFloatBuffer(16); - GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, after); - assertEquals(0.5f, after.get(0), 0.001f, "After COMPILE_AND_EXECUTE: scale remains"); - - GL11.glLoadIdentity(); - } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListIdAllocatorTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListIdAllocatorTest.java index dde39d2c6..294c91980 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListIdAllocatorTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListIdAllocatorTest.java @@ -1,16 +1,15 @@ package com.gtnewhorizons.angelica.glsm; import com.gtnewhorizons.angelica.glsm.testutil.TestThreads; +import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import org.junit.jupiter.api.Test; 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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -39,25 +38,14 @@ class DisplayListIdAllocatorTest { } @Test - void zeroNeverReturned() { - DisplayListIDAllocator alloc = new DisplayListIDAllocator(1); - // Allocate enough IDs to fill the first word (32 IDs). None should be 0. - for (int i = 0; i < 31; i++) { - int id = alloc.allocRange(1); - assertNotEquals(0, id, "ID 0 must never be returned"); - assertTrue(id > 0, "All returned IDs must be positive"); + void singleAllocsAreSequentialFromOne() { + // Sequential from 1 across several words: 0 is never handed out and no id repeats + final DisplayListIDAllocator alloc = new DisplayListIDAllocator(1); + for (int expected = 1; expected <= 500; expected++) { + assertEquals(expected, alloc.allocRange(1)); } } - @Test - void singleAllocReturnsSequential() { - DisplayListIDAllocator alloc = new DisplayListIDAllocator(); - // First alloc should be 1 (0 is reserved) - assertEquals(1, alloc.allocRange(1)); - assertEquals(2, alloc.allocRange(1)); - assertEquals(3, alloc.allocRange(1)); - } - @Test void isAllocatedTracksState() { DisplayListIDAllocator alloc = new DisplayListIDAllocator(); @@ -159,9 +147,9 @@ class DisplayListIdAllocatorTest { @Test void freeZeroIsNoOp() { DisplayListIDAllocator alloc = new DisplayListIDAllocator(); - // Should not throw alloc.free(0); alloc.free(-1); + assertEquals(1, alloc.allocRange(1), "freeing the reserved id 0 must not make it allocatable"); } @Test @@ -205,17 +193,6 @@ class DisplayListIdAllocatorTest { } } - @Test - void noDoubleAllocation() { - DisplayListIDAllocator alloc = new DisplayListIDAllocator(); - // Allocate many IDs and verify uniqueness - Set seen = new HashSet<>(); - for (int i = 0; i < 500; i++) { - int id = alloc.allocRange(1); - assertTrue(seen.add(id), "Duplicate ID returned: " + id); - } - } - @Test void rangeAfterSingleAllocs() { DisplayListIDAllocator alloc = new DisplayListIDAllocator(); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListMatrixRestorationTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListMatrixRestorationTest.java index f2cd35d39..3f70f790a 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListMatrixRestorationTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListMatrixRestorationTest.java @@ -54,9 +54,7 @@ class DisplayListMatrixRestorationTest extends DisplayListTestFixture { } private static float translation(int target) { - FloatBuffer b = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(target, b); - return b.get(12); + return GlTestSupport.matrix(target).get(12); } @Test diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListTestHelper.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListTestHelper.java index f00b4dc96..f1f5d7a92 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListTestHelper.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/DisplayListTestHelper.java @@ -1,10 +1,17 @@ package com.gtnewhorizons.angelica.glsm; import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator; +import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager; import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList; import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder; +import net.minecraft.client.renderer.Tessellator; +import org.joml.Matrix4f; +import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; +import java.nio.FloatBuffer; + +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; /** @@ -62,4 +69,58 @@ public final class DisplayListTestHelper { assertNotNull(compiled, "Display list should be compiled"); return compiled; } + + /** Calls {@code list} on an identity modelview and returns the resulting matrix. The caller's matrix is restored. */ + public static FloatBuffer modelviewAfterCall(int list) { + GLStateManager.glPushMatrix(); + try { + GLStateManager.glLoadIdentity(); + GLStateManager.glCallList(list); + return GlTestSupport.modelview(); + } finally { + GLStateManager.glPopMatrix(); + } + } + + public static void assertMatrixEquals(Matrix4f expected, FloatBuffer actual, String message) { + final FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16); + expected.get(expectedBuf); + for (int i = 0; i < 16; i++) { + assertEquals(expectedBuf.get(i), actual.get(i), 0.0001f, message + " (element " + i + ")"); + } + } + + public static void assertModelviewAfterCall(int list, Matrix4f expected, String message) { + assertMatrixEquals(expected, modelviewAfterCall(list), message); + } + + /** Immediate-mode quad from the origin to (size, size) at z = 0. */ + public static void immediateQuad(float size) { + GLStateManager.glBegin(GL11.GL_QUADS); + GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f); + GLStateManager.glVertex3f(size, 0.0f, 0.0f); + GLStateManager.glVertex3f(size, size, 0.0f); + GLStateManager.glVertex3f(0.0f, size, 0.0f); + GLStateManager.glEnd(); + } + + /** Textured quad written straight into the vanilla Tessellator's raw buffer and handed to the draw interceptor. */ + public static void rawQuad(float size) { + final Tessellator tess = Tessellator.instance; + final float[][] corners = { { 0.0f, 0.0f }, { size, 0.0f }, { size, size }, { 0.0f, size } }; + tess.rawBuffer = new int[4 * 8]; + for (int i = 0; i < 4; i++) { + final int base = i * 8; + tess.rawBuffer[base] = Float.floatToRawIntBits(corners[i][0]); + tess.rawBuffer[base + 1] = Float.floatToRawIntBits(corners[i][1]); + tess.rawBuffer[base + 3] = Float.floatToRawIntBits(i == 1 || i == 2 ? 1.0f : 0.0f); + tess.rawBuffer[base + 4] = Float.floatToRawIntBits(i >= 2 ? 1.0f : 0.0f); + } + tess.isDrawing = true; + tess.drawMode = GL11.GL_QUADS; + tess.hasTexture = true; + tess.rawBufferIndex = 4 * 8; + tess.vertexCount = 4; + TessellatorManager.interceptDraw(tess); + } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/FeedbackManagerTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/FeedbackManagerTest.java index df6c437ca..125aec597 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/FeedbackManagerTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/FeedbackManagerTest.java @@ -3,12 +3,16 @@ package com.gtnewhorizons.angelica.glsm; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL30; import java.nio.FloatBuffer; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -97,68 +101,23 @@ class FeedbackManagerTest { assertEquals(42.0f, buf.get(1), "Second token should be the pass-through value"); } - @Test - void testTriangleFeedback_GL2D() { - final FloatBuffer buf = BufferUtils.createFloatBuffer(128); - FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf); - FeedbackManager.glRenderMode(GL11.GL_FEEDBACK); - - // Identity MV/proj, viewport (0,0,800,600) - GLStateManager.getModelViewMatrix().identity(); - GLStateManager.getProjectionMatrix().identity(); - GLStateManager.getViewportState().setViewPort(0, 0, 800, 600); - GLStateManager.getViewportState().setDepthRange(0.0, 1.0); - - // Upload 3 vertices forming a triangle - final int vao = GL30.glGenVertexArrays(); - GLStateManager.glBindVertexArray(vao); - - // NDC vertices: (0,0,0), (1,0,0), (0,1,0) - // With identity MVP and viewport 800x600: - // NDC(0,0) → window(400, 300) - // NDC(1,0) → window(800, 300) - // NDC(0,1) → window(400, 600) - final float[] verts = { 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f, 0f }; - final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(9); - vertBuf.put(verts).flip(); - - final int vbo = GL15.glGenBuffers(); - GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertBuf, GL15.GL_STATIC_DRAW); - GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0); - GLStateManager.glEnableVertexAttribArray(0); - - FeedbackManager.processDrawArrays(GL11.GL_TRIANGLES, 0, 3); - - final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER); - - // GL_2D: POLYGON_TOKEN, 3, x0, y0, x1, y1, x2, y2 = 8 floats - assertEquals(8, count, "Triangle in GL_2D should produce 8 floats"); - - assertEquals(GL11.GL_POLYGON_TOKEN, buf.get(0), 0.001f); - assertEquals(3.0f, buf.get(1), 0.001f); - - // Vertex 0: NDC(0,0) → window(400, 300) - assertEquals(400.0f, buf.get(2), 0.01f); - assertEquals(300.0f, buf.get(3), 0.01f); - - // Vertex 1: NDC(1,0) → window(800, 300) - assertEquals(800.0f, buf.get(4), 0.01f); - assertEquals(300.0f, buf.get(5), 0.01f); - - // Vertex 2: NDC(0,1) → window(400, 600) - assertEquals(400.0f, buf.get(6), 0.01f); - assertEquals(600.0f, buf.get(7), 0.01f); - - // Cleanup - GLStateManager.glDisableVertexAttribArray(0); - GLStateManager.glBindVertexArray(0); - GL15.glDeleteBuffers(vbo); - GLStateManager.glDeleteVertexArrays(vao); + // Identity MVP on an 800x600 viewport: NDC (0,0) -> window (400,300), (1,0) -> (800,300), (0,1) -> (400,600) + static Stream primitives() { + return Stream.of( + Arguments.of("triangle", GL11.GL_TRIANGLES, + new float[] { 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f, 0f }, + new float[] { GL11.GL_POLYGON_TOKEN, 3f, 400f, 300f, 800f, 300f, 400f, 600f }), + Arguments.of("polygon", GL11.GL_POLYGON, + new float[] { 0f, 0f, 0f, 1f, 0f, 0f, 1f, 1f, 0f, 0f, 1f, 0f }, + new float[] { GL11.GL_POLYGON_TOKEN, 4f, 400f, 300f, 800f, 300f, 800f, 600f, 400f, 600f }), + Arguments.of("quad strip", GL11.GL_QUAD_STRIP, + new float[] { 0f, 0f, 0f, 0f, 1f, 0f, 1f, 0f, 0f, 1f, 1f, 0f }, + new float[] { GL11.GL_POLYGON_TOKEN, 4f, 400f, 300f, 400f, 600f, 800f, 600f, 800f, 300f })); } - @Test - void testPolygonFeedback_GL2D() { + @ParameterizedTest(name = "{0}") + @MethodSource("primitives") + void testPrimitiveFeedback_GL2D(String name, int mode, float[] verts, float[] expected) { final FloatBuffer buf = BufferUtils.createFloatBuffer(128); FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf); FeedbackManager.glRenderMode(GL11.GL_FEEDBACK); @@ -171,12 +130,6 @@ class FeedbackManagerTest { final int vao = GL30.glGenVertexArrays(); GLStateManager.glBindVertexArray(vao); - final float[] verts = { - 0f, 0f, 0f, - 1f, 0f, 0f, - 1f, 1f, 0f, - 0f, 1f, 0f - }; final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(verts.length); vertBuf.put(verts).flip(); @@ -186,72 +139,15 @@ class FeedbackManagerTest { GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0); GLStateManager.glEnableVertexAttribArray(0); - FeedbackManager.processDrawArrays(GL11.GL_POLYGON, 0, 4); + FeedbackManager.processDrawArrays(mode, 0, verts.length / 3); final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER); - assertEquals(10, count, "Polygon in GL_2D should produce 10 floats"); - assertEquals(GL11.GL_POLYGON_TOKEN, buf.get(0), 0.001f); - assertEquals(4.0f, buf.get(1), 0.001f); - assertEquals(400.0f, buf.get(2), 0.01f); - assertEquals(300.0f, buf.get(3), 0.01f); - assertEquals(800.0f, buf.get(4), 0.01f); - assertEquals(300.0f, buf.get(5), 0.01f); - assertEquals(800.0f, buf.get(6), 0.01f); - assertEquals(600.0f, buf.get(7), 0.01f); - assertEquals(400.0f, buf.get(8), 0.01f); - assertEquals(600.0f, buf.get(9), 0.01f); - - GLStateManager.glDisableVertexAttribArray(0); - GLStateManager.glBindVertexArray(0); - GL15.glDeleteBuffers(vbo); - GLStateManager.glDeleteVertexArrays(vao); - } - - @Test - void testQuadStripFeedback_GL2D() { - final FloatBuffer buf = BufferUtils.createFloatBuffer(128); - FeedbackManager.glFeedbackBuffer(GL11.GL_2D, buf); - FeedbackManager.glRenderMode(GL11.GL_FEEDBACK); - - GLStateManager.getModelViewMatrix().identity(); - GLStateManager.getProjectionMatrix().identity(); - GLStateManager.getViewportState().setViewPort(0, 0, 800, 600); - GLStateManager.getViewportState().setDepthRange(0.0, 1.0); - - final int vao = GL30.glGenVertexArrays(); - GLStateManager.glBindVertexArray(vao); - - final float[] verts = { - 0f, 0f, 0f, - 0f, 1f, 0f, - 1f, 0f, 0f, - 1f, 1f, 0f - }; - final FloatBuffer vertBuf = BufferUtils.createFloatBuffer(verts.length); - vertBuf.put(verts).flip(); - - final int vbo = GL15.glGenBuffers(); - GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertBuf, GL15.GL_STATIC_DRAW); - GLStateManager.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 12, 0); - GLStateManager.glEnableVertexAttribArray(0); - - FeedbackManager.processDrawArrays(GL11.GL_QUAD_STRIP, 0, 4); - - final int count = FeedbackManager.glRenderMode(GL11.GL_RENDER); - - assertEquals(10, count, "Quad strip in GL_2D should produce 10 floats"); - assertEquals(GL11.GL_POLYGON_TOKEN, buf.get(0), 0.001f); - assertEquals(4.0f, buf.get(1), 0.001f); - assertEquals(400.0f, buf.get(2), 0.01f); - assertEquals(300.0f, buf.get(3), 0.01f); - assertEquals(400.0f, buf.get(4), 0.01f); - assertEquals(600.0f, buf.get(5), 0.01f); - assertEquals(800.0f, buf.get(6), 0.01f); - assertEquals(600.0f, buf.get(7), 0.01f); - assertEquals(800.0f, buf.get(8), 0.01f); - assertEquals(300.0f, buf.get(9), 0.01f); + // GL_2D: POLYGON_TOKEN, vertex count, then x, y per vertex + assertEquals(expected.length, count, name + " float count"); + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], buf.get(i), 0.01f, name + " feedback value " + i); + } GLStateManager.glDisableVertexAttribArray(0); GLStateManager.glBindVertexArray(0); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_BlendFuncUnknownState_GLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_BlendFuncUnknownState_GLTest.java index 5ffd53fc1..40b41a1c3 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_BlendFuncUnknownState_GLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_BlendFuncUnknownState_GLTest.java @@ -22,7 +22,6 @@ class GLSM_BlendFuncUnknownState_GLTest { @BeforeEach void requireBufferBlending() { assumeTrue(RenderSystem.supportsBufferBlending()); - drainGlErrors(); } @AfterEach @@ -30,11 +29,6 @@ class GLSM_BlendFuncUnknownState_GLTest { if (!RenderSystem.supportsBufferBlending()) return; GLStateManager.tryBlendFuncSeparate(GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ZERO, GL11.GL_ZERO); GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - drainGlErrors(); - } - - private static void drainGlErrors() { - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} } private static void seedVanillaSeparate() { @@ -59,7 +53,6 @@ class GLSM_BlendFuncUnknownState_GLTest { assertBufferBlend(buffer, VANILLA_SRC_RGB, VANILLA_DST_RGB, VANILLA_SRC_ALPHA, VANILLA_DST_ALPHA); } assertFalse(GLStateManager.getBlendState().isFuncUnknown(), "forward must clear the unknown flag"); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } @Test diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_BlendUnknownState_UnitTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_BlendUnknownState_UnitTest.java index d951fc392..f9ba711cc 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_BlendUnknownState_UnitTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_BlendUnknownState_UnitTest.java @@ -3,11 +3,13 @@ package com.gtnewhorizons.angelica.glsm; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL30; import static com.gtnewhorizons.angelica.util.GLSMUtil.resetGLState; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -51,46 +53,28 @@ class GLSM_BlendUnknownState_UnitTest { assertTrue(GL11.glIsEnabled(GL11.GL_BLEND), "GL blend should be re-enabled after setUnknownState"); } - @Test - void testDisableBufferBlendInvalidatesCache() { - GLStateManager.enableBlend(); - assertTrue(GL11.glIsEnabled(GL11.GL_BLEND)); + @ParameterizedTest(name = "global blend enabled={0}") + @ValueSource(booleans = {true, false}) + void perBufferBlendToggleInvalidatesCache(boolean enabled) { + setBlend(enabled); + assertEquals(enabled, GL11.glIsEnabled(GL11.GL_BLEND)); - // Use RenderSystem which should call setUnknownState internally - RenderSystem.disableBufferBlend(0); + // Flips buffer 0 behind the cache and marks it unknown, so the repeated global call must reach the driver + if (enabled) { + RenderSystem.disableBufferBlend(0); + } else { + RenderSystem.enableBufferBlend(0); + } - // Now re-enable via GLSM — should issue the GL call - GLStateManager.enableBlend(); - assertTrue(GL11.glIsEnabled(GL11.GL_BLEND), "Blend should be re-enabled after disableBufferBlend"); + setBlend(enabled); + assertEquals(enabled, GL11.glIsEnabled(GL11.GL_BLEND), "global blend state must be re-issued after a per-buffer toggle"); } - @Test - void testEnableBufferBlendInvalidatesCache() { - // Start with blend disabled - GLStateManager.disableBlend(); - assertFalse(GL11.glIsEnabled(GL11.GL_BLEND)); - - // Per-buffer enable bypasses cache - RenderSystem.enableBufferBlend(0); - - // Disable via GLSM — should issue the GL call despite cache saying disabled - GLStateManager.disableBlend(); - assertFalse(GL11.glIsEnabled(GL11.GL_BLEND), "Blend should be disabled after enableBufferBlend + disableBlend"); - } - - @Test - void testUnknownStateClearedAfterSetEnabled() { - GLStateManager.enableBlend(); - - // Mark unknown - GLStateManager.getBlendMode().setUnknownState(); - - // First setEnabled clears the unknown flag - GLStateManager.enableBlend(); - - // Second setEnabled should use normal caching (no redundant GL call) - // Verify state is consistent - assertTrue(GL11.glIsEnabled(GL11.GL_BLEND)); - assertTrue(GLStateManager.getBlendMode().isEnabled()); + private static void setBlend(boolean enabled) { + if (enabled) { + GLStateManager.enableBlend(); + } else { + GLStateManager.disableBlend(); + } } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ClientArrays_GLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ClientArrays_GLTest.java new file mode 100644 index 000000000..77def71a7 --- /dev/null +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ClientArrays_GLTest.java @@ -0,0 +1,672 @@ +package com.gtnewhorizons.angelica.glsm; + +import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities; +import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags; +import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage; +import com.gtnewhorizons.angelica.glsm.ffp.VAOManager; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL14; +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL33; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.CLIENT_ARRAY_CAPS; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.assertEnableRoundTrip; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.drainClientAttribStack; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.hex; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeFalse; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** Conventional client arrays and the client attrib stack, emulated on a core-profile vertex array object. */ +@GLCoreTest +public class GLSM_ClientArrays_GLTest { + + private int savedVao; + private int vao; + private int vbo; + + @BeforeEach + void bindFreshVertexArray() { + savedVao = GLStateManager.getBoundVAO(); + vao = GLStateManager.glGenVertexArrays(); + vbo = GLStateManager.glGenBuffers(); + GLStateManager.glBindVertexArray(vao); + } + + @AfterEach + void deleteVertexArray() { + GLStateManager.glBindVertexArray(savedVao); + GLStateManager.glDeleteVertexArrays(vao); + GLStateManager.glDeleteBuffers(vbo); + } + + private static boolean attribHasSize3(Usage usage) { + final VAOManager.Attrib attrib = GLStateManager.ctx().vaos.attrib(usage.getAttributeLocation()); + return attrib != null && attrib.size == 3; + } + + private static boolean positionArrayEnabled() { + final VAOManager.Attrib position = GLStateManager.ctx().vaos.attrib(Usage.POSITION.getAttributeLocation()); + return position != null && position.enabled; + } + + private static boolean colorArrayEnabled() { + return (GLStateManager.ctx().vaos.getVertexFlags() & VertexFlags.COLOR_BIT) != 0; + } + + private static int driverAttrib(int index, int pname) { + final IntBuffer out = BufferUtils.createIntBuffer(16); + GL20.glGetVertexAttrib(index, pname, out); + return out.get(0); + } + + private static void assertClientArray(String name, int sizePname, int size, int typePname, int type, int stridePname, int stride, int bindingPname, int binding) { + if (sizePname != 0) assertEquals(size, GLStateManager.glGetInteger(sizePname), name + " size"); + if (typePname != 0) assertEquals(type, GLStateManager.glGetInteger(typePname), name + " type"); + assertEquals(stride, GLStateManager.glGetInteger(stridePname), name + " stride"); + assertEquals(binding, GLStateManager.glGetInteger(bindingPname), name + " buffer binding"); + } + + private static void assertVertexArray(String name, int size, int type, int stride, int binding) { + assertClientArray(name, GL11.GL_VERTEX_ARRAY_SIZE, size, GL11.GL_VERTEX_ARRAY_TYPE, type, GL11.GL_VERTEX_ARRAY_STRIDE, stride, GL15.GL_VERTEX_ARRAY_BUFFER_BINDING, binding); + } + + private static void assertTexCoordArray(String name, int size, int type, int stride, int binding) { + assertClientArray(name, GL11.GL_TEXTURE_COORD_ARRAY_SIZE, size, GL11.GL_TEXTURE_COORD_ARRAY_TYPE, type, GL11.GL_TEXTURE_COORD_ARRAY_STRIDE, stride, + GL15.GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, binding); + } + + @Test + void vertexArrayStateRestoredOnlyWithVertexArrayBit() { + final int position = Usage.POSITION.getAttributeLocation(); + try { + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + final int flagsBefore = GLStateManager.ctx().vaos.getVertexFlags(); + assertFalse(positionArrayEnabled()); + assertFalse(colorArrayEnabled()); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); + assertTrue(positionArrayEnabled()); + assertTrue(colorArrayEnabled()); + GLStateManager.glPopClientAttrib(); + assertFalse(positionArrayEnabled(), "pop disables an array enabled inside the push"); + assertEquals(GL11.GL_FALSE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED)); + assertFalse(colorArrayEnabled()); + assertEquals(flagsBefore, GLStateManager.ctx().vaos.getVertexFlags()); + assertEquals(0, GLStateManager.getClientActiveTextureUnit()); + + GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glPopClientAttrib(); + assertTrue(positionArrayEnabled(), "pop re-enables an array disabled inside the push"); + assertEquals(GL11.GL_TRUE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED)); + GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); + GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.glPopClientAttrib(); + assertTrue(positionArrayEnabled(), "a pixel-store push must not restore array enables"); + assertTrue(colorArrayEnabled()); + assertEquals(1, GLStateManager.getClientActiveTextureUnit()); + } finally { + drainClientAttribStack(); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY); + } + } + + @Test + void clientActiveTextureRoundTrips() { + try { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); + assertEquals(GLStateManager.MAX_TEXTURE_COORDS, GLStateManager.glGetInteger(GL20.GL_MAX_TEXTURE_COORDS)); + + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); + assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE)); + GLStateManager.glClientActiveTexture(0); + assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE)); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0 + GLStateManager.MAX_TEXTURE_UNITS); + assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE)); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0 + GLStateManager.MAX_TEXTURE_COORDS); + assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE), "units past GL_MAX_TEXTURE_COORDS are rejected"); + assertEquals(1, GLStateManager.getClientActiveTextureUnit()); + + GLStateManager.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0L); + final VAOManager.Attrib attrib = GLStateManager.ctx().vaos.attrib(Usage.SECONDARY_UV.getAttributeLocation()); + assertNotNull(attrib); + assertEquals(2, attrib.size); + + assumeTrue(GLStateManager.MAX_TEXTURE_COORDS > 5); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE5); + assertEquals(GL13.GL_TEXTURE5, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE)); + GLStateManager.glTexCoordPointer(3, GL11.GL_FLOAT, 0, 0L); + assertEquals(3, GLStateManager.glGetInteger(GL11.GL_TEXTURE_COORD_ARRAY_SIZE)); + assertFalse(attribHasSize3(Usage.PRIMARY_UV)); + assertFalse(attribHasSize3(Usage.SECONDARY_UV)); + } finally { + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + } + } + + @Test + void conventionalPointerSurvivesGenericOverride() { + final int position = Usage.POSITION.getAttributeLocation(); + try { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); + + GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L); + GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glVertexAttribPointer(position, 2, GL11.GL_FLOAT, false, 8, 4L); + assertEquals(3, VAOManager.get(position).size, "disabled generic pointer must not displace an enabled conventional array"); + assertEquals(3, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE)); + + GLStateManager.glEnableVertexAttribArray(position); + assertEquals(2, VAOManager.get(position).size, "enabled generic attribute wins"); + assertTrue(VAOManager.get(position).genericPointer); + assertEquals(2, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE)); + assertEquals(8, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_STRIDE)); + + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + GLStateManager.glDisableVertexAttribArray(position); + assertEquals(3, VAOManager.get(position).size, "conventional pointer must come back when the generic attribute is disabled"); + assertEquals(12, VAOManager.get(position).stride); + assertFalse(VAOManager.get(position).genericPointer); + assertEquals(3, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE)); + assertEquals(12, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_STRIDE)); + assertEquals(vbo, driverAttrib(position, GL15.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)); + assertEquals(GL11.GL_TRUE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED), "conventional array stays enabled at draw level"); + assertTrue(GLStateManager.glIsEnabled(GL11.GL_VERTEX_ARRAY)); + assertEquals(0, GLStateManager.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING), "repoint must restore the array buffer binding"); + } finally { + GLStateManager.glDisableVertexAttribArray(position); + GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + } + } + + @Test + void conventionalPointerSetUnderEnabledGenericAppliesOnceGenericIsDisabled() { + final int position = Usage.POSITION.getAttributeLocation(); + try { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); + + GLStateManager.glVertexAttribPointer(position, 2, GL11.GL_FLOAT, false, 8, 0L); + GLStateManager.glEnableVertexAttribArray(position); + GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L); + GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); + assertEquals(2, VAOManager.get(position).size); + assertEquals(2, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE)); + + GLStateManager.glDisableVertexAttribArray(position); + assertEquals(3, VAOManager.get(position).size); + assertEquals(3, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE)); + assertEquals(GL11.GL_TRUE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED)); + + GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); + assertEquals(GL11.GL_FALSE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED)); + assertFalse(positionArrayEnabled()); + } finally { + GLStateManager.glDisableVertexAttribArray(position); + GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + } + } + + @Test + void drawVertexFlagsDoNotAnswerClientArrayEnables() { + try { + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + VAOManager.setCurrentVertexFlags(VertexFlags.COLOR_BIT | VertexFlags.NORMAL_BIT | VertexFlags.TEXTURE_BIT); + assertFalse(GLStateManager.glIsEnabled(GL11.GL_COLOR_ARRAY)); + assertFalse(GLStateManager.glIsEnabled(GL11.GL_NORMAL_ARRAY)); + assertFalse(GLStateManager.glIsEnabled(GL11.GL_TEXTURE_COORD_ARRAY)); + + GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY); + VAOManager.setCurrentVertexFlags(0); + assertTrue(GLStateManager.glIsEnabled(GL11.GL_COLOR_ARRAY)); + } finally { + GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY); + VAOManager.setCurrentVertexFlags(0); + } + } + + @Test + void clientArrayPointerQueriesAreAnsweredFromCache() { + final int vaoB = GLStateManager.glGenVertexArrays(); + final IntBuffer ints = BufferUtils.createIntBuffer(16); + try { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + + assertVertexArray("default vertex", 4, GL11.GL_FLOAT, 0, 0); + assertClientArray("default normal", 0, 0, GL11.GL_NORMAL_ARRAY_TYPE, GL11.GL_FLOAT, GL11.GL_NORMAL_ARRAY_STRIDE, 0, GL15.GL_NORMAL_ARRAY_BUFFER_BINDING, 0); + assertClientArray("default color", GL11.GL_COLOR_ARRAY_SIZE, 4, GL11.GL_COLOR_ARRAY_TYPE, GL11.GL_FLOAT, GL11.GL_COLOR_ARRAY_STRIDE, 0, GL15.GL_COLOR_ARRAY_BUFFER_BINDING, 0); + assertClientArray("default secondary color", GL14.GL_SECONDARY_COLOR_ARRAY_SIZE, 3, GL14.GL_SECONDARY_COLOR_ARRAY_TYPE, GL11.GL_FLOAT, + GL14.GL_SECONDARY_COLOR_ARRAY_STRIDE, 0, GL15.GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING, 0); + assertClientArray("default fog coord", 0, 0, GL14.GL_FOG_COORDINATE_ARRAY_TYPE, GL11.GL_FLOAT, GL14.GL_FOG_COORDINATE_ARRAY_STRIDE, 0, + GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING, 0); + assertClientArray("default index", 0, 0, GL11.GL_INDEX_ARRAY_TYPE, GL11.GL_FLOAT, GL11.GL_INDEX_ARRAY_STRIDE, 0, GL15.GL_INDEX_ARRAY_BUFFER_BINDING, 0); + assertClientArray("default edge flag", 0, 0, 0, 0, GL11.GL_EDGE_FLAG_ARRAY_STRIDE, 0, GL15.GL_EDGE_FLAG_ARRAY_BUFFER_BINDING, 0); + assertTexCoordArray("default texcoord", 4, GL11.GL_FLOAT, 0, 0); + assertNull(GLStateManager.glGetPointer(GL11.GL_VERTEX_ARRAY_POINTER, 4)); + + GLStateManager.glVertexPointer(3, GL11.GL_SHORT, 20, 8L); + GLStateManager.glNormalPointer(GL11.GL_BYTE, 16, 4L); + GLStateManager.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, 16, 12L); + GLStateManager.glSecondaryColorPointer(3, GL11.GL_FLOAT, 12, 0L); + GLStateManager.glFogCoordPointer(GL11.GL_FLOAT, 4, 0L); + GLStateManager.glEdgeFlagPointer(7, 0L); + GLStateManager.glTexCoordPointer(2, GL11.GL_FLOAT, 8, 0L); + + assertVertexArray("vertex", 3, GL11.GL_SHORT, 20, vbo); + assertClientArray("normal", 0, 0, GL11.GL_NORMAL_ARRAY_TYPE, GL11.GL_BYTE, GL11.GL_NORMAL_ARRAY_STRIDE, 16, GL15.GL_NORMAL_ARRAY_BUFFER_BINDING, vbo); + assertClientArray("color", GL11.GL_COLOR_ARRAY_SIZE, 4, GL11.GL_COLOR_ARRAY_TYPE, GL11.GL_UNSIGNED_BYTE, GL11.GL_COLOR_ARRAY_STRIDE, 16, + GL15.GL_COLOR_ARRAY_BUFFER_BINDING, vbo); + assertClientArray("secondary color", GL14.GL_SECONDARY_COLOR_ARRAY_SIZE, 3, GL14.GL_SECONDARY_COLOR_ARRAY_TYPE, GL11.GL_FLOAT, + GL14.GL_SECONDARY_COLOR_ARRAY_STRIDE, 12, GL15.GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING, vbo); + assertClientArray("fog coord", 0, 0, GL14.GL_FOG_COORDINATE_ARRAY_TYPE, GL11.GL_FLOAT, GL14.GL_FOG_COORDINATE_ARRAY_STRIDE, 4, + GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING, vbo); + assertClientArray("edge flag", 0, 0, 0, 0, GL11.GL_EDGE_FLAG_ARRAY_STRIDE, 7, GL15.GL_EDGE_FLAG_ARRAY_BUFFER_BINDING, vbo); + assertTexCoordArray("texcoord unit 0", 2, GL11.GL_FLOAT, 8, vbo); + + assertEquals(3.0f, GLStateManager.glGetFloat(GL11.GL_VERTEX_ARRAY_SIZE)); + GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_STRIDE, ints); + assertEquals(20, ints.get(0)); + assertEquals(8L, MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_VERTEX_ARRAY_POINTER, 4))); + assertEquals(12L, MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_COLOR_ARRAY_POINTER, 4))); + + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0 + GLStateManager.MAX_TEXTURE_COORDS - 1); + assertTexCoordArray("unset texcoord unit", 4, GL11.GL_FLOAT, 0, 0); + GLStateManager.glTexCoordPointer(3, GL11.GL_SHORT, 6, 0L); + assertTexCoordArray("last texcoord unit", 3, GL11.GL_SHORT, 6, vbo); + assertFalse(attribHasSize3(Usage.PRIMARY_UV)); + assertFalse(attribHasSize3(Usage.SECONDARY_UV)); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + assertTexCoordArray("texcoord unit 0 again", 2, GL11.GL_FLOAT, 8, vbo); + + GLStateManager.glBindVertexArray(vaoB); + assertVertexArray("other vao", 4, GL11.GL_FLOAT, 0, 0); + final FloatBuffer client = BufferUtils.createFloatBuffer(12); + GLStateManager.glVertexPointer(2, 0, client); + assertVertexArray("client pointer", 2, GL11.GL_FLOAT, 0, 0); + assertEquals(MemoryUtilities.memAddress(client), MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_VERTEX_ARRAY_POINTER, 4))); + GLStateManager.glBindVertexArray(vao); + assertVertexArray("first vao again", 3, GL11.GL_SHORT, 20, vbo); + } finally { + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + GLStateManager.glDeleteVertexArrays(vaoB); + } + } + + @Test + void interleavedArraysSetClientStateWithoutAnActiveFfpProgram() { + try { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + assumeFalse(GLStateManager.ctx().ffp.isActive()); + + GLStateManager.glInterleavedArrays(GL11.GL_C4UB_V3F, 0, 0L); + assertTrue(GLStateManager.glIsEnabled(GL11.GL_VERTEX_ARRAY)); + assertTrue(GLStateManager.glIsEnabled(GL11.GL_COLOR_ARRAY)); + assertFalse(GLStateManager.glIsEnabled(GL11.GL_NORMAL_ARRAY)); + assertVertexArray("interleaved vertex", 3, GL11.GL_FLOAT, 16, vbo); + assertEquals(GL11.GL_UNSIGNED_BYTE, GLStateManager.glGetInteger(GL11.GL_COLOR_ARRAY_TYPE)); + } finally { + for (final int cap : CLIENT_ARRAY_CAPS) { + GLStateManager.glDisableClientState(cap); + } + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + } + } + + @Test + void clientVertexArrayBitRestoresPointersEnablesAndBindings() { + final int vaoB = GLStateManager.glGenVertexArrays(); + final int vbo2 = GLStateManager.glGenBuffers(); + final int ebo = GLStateManager.glGenBuffers(); + final int position = Usage.POSITION.getAttributeLocation(); + final int color = Usage.COLOR.getAttributeLocation(); + final int normal = Usage.NORMAL.getAttributeLocation(); + try { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); + GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L); + GLStateManager.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, 16, 12L); + GLStateManager.glFogCoordPointer(GL11.GL_FLOAT, 4, 0L); + GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo2); + GLStateManager.glVertexPointer(2, GL11.GL_SHORT, 4, 4L); + GLStateManager.glFogCoordPointer(GL11.GL_FLOAT, 8, 0L); + GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY); + GLStateManager.glEnableClientState(GL11.GL_NORMAL_ARRAY); + GLStateManager.glVertexAttribPointer(5, 1, GL11.GL_FLOAT, false, 4, 0L); + GLStateManager.glEnableVertexAttribArray(5); + GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.glBindVertexArray(vaoB); + GLStateManager.glPopClientAttrib(); + + assertEquals(vao, GLStateManager.getBoundVAO()); + assertEquals(vbo, GLStateManager.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING)); + assertEquals(ebo, GLStateManager.glGetInteger(GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING)); + assertEquals(ebo, GL11.glGetInteger(GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING)); + assertEquals(GL13.GL_TEXTURE0, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE)); + assertVertexArray("restored vertex", 3, GL11.GL_FLOAT, 12, vbo); + assertEquals(4, GLStateManager.glGetInteger(GL14.GL_FOG_COORDINATE_ARRAY_STRIDE)); + assertEquals(vbo, GLStateManager.glGetInteger(GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING)); + assertTrue(GLStateManager.glIsEnabled(GL11.GL_COLOR_ARRAY)); + assertFalse(GLStateManager.glIsEnabled(GL11.GL_NORMAL_ARRAY)); + assertTrue(colorArrayEnabled()); + + assertEquals(3, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_SIZE)); + assertEquals(GL11.GL_FLOAT, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_TYPE)); + assertEquals(12, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_STRIDE)); + assertEquals(vbo, driverAttrib(position, GL15.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)); + assertEquals(GL11.GL_TRUE, driverAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED)); + assertEquals(GL11.GL_TRUE, driverAttrib(color, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED)); + assertEquals(GL11.GL_FALSE, driverAttrib(normal, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED)); + assertEquals(GL11.GL_FALSE, driverAttrib(5, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED)); + + GLStateManager.glBindVertexArray(vaoB); + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glBindVertexArray(vao); + GLStateManager.glDeleteVertexArrays(vaoB); + GLStateManager.glPopClientAttrib(); + assertEquals(vao, GLStateManager.getBoundVAO(), "a deleted VAO must not be restored"); + assertVertexArray("after dead-vao pop", 3, GL11.GL_FLOAT, 12, vbo); + } finally { + drainClientAttribStack(); + GLStateManager.glBindVertexArray(vao); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + for (final int cap : CLIENT_ARRAY_CAPS) { + GLStateManager.glDisableClientState(cap); + } + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + GLStateManager.glDeleteVertexArrays(vaoB); + GLStateManager.glDeleteBuffers(vbo2); + GLStateManager.glDeleteBuffers(ebo); + } + } + + @Test + void enabledGenericAttributeWinsWithoutAPointer() { + final int position = Usage.POSITION.getAttributeLocation(); + try { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); + GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L); + GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); + assertFalse(VAOManager.get(position).genericPointer); + + GLStateManager.glEnableVertexAttribArray(position); + assertTrue(VAOManager.get(position).genericPointer, "generic 0 supersedes position on its enable bit alone"); + GLStateManager.glDisableVertexAttribArray(position); + assertFalse(VAOManager.get(position).genericPointer); + assertEquals(3, VAOManager.get(position).size); + } finally { + GLStateManager.glDisableVertexAttribArray(position); + GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + } + } + + @Test + void deletedBufferIsDetachedFromTheBoundVertexArray() { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); + GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 4L); + GLStateManager.glFogCoordPointer(GL11.GL_FLOAT, 4, 0L); + assertEquals(vbo, GLStateManager.glGetInteger(GL15.GL_VERTEX_ARRAY_BUFFER_BINDING)); + + GLStateManager.glDeleteBuffers(vbo); + assertEquals(0, GLStateManager.glGetInteger(GL15.GL_VERTEX_ARRAY_BUFFER_BINDING)); + assertEquals(0, GLStateManager.glGetInteger(GL15.GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING)); + assertEquals(12, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_STRIDE), "offset and stride survive the detach"); + assertEquals(3, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_SIZE)); + } + + @Test + void shadowedPointerIsNotRepointedAtAReusedBufferName() { + final int other = GLStateManager.glGenVertexArrays(); + final int conventional = GLStateManager.glGenBuffers(); + final int generic = GLStateManager.glGenBuffers(); + final int position = Usage.POSITION.getAttributeLocation(); + int reused = 0; + try { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, conventional); + GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L); + GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, generic); + GLStateManager.glVertexAttribPointer(position, 2, GL11.GL_FLOAT, false, 8, 0L); + GLStateManager.glEnableVertexAttribArray(position); + + GLStateManager.glBindVertexArray(other); + GLStateManager.glDeleteBuffers(conventional); + reused = GLStateManager.glGenBuffers(); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, reused); + assumeTrue(reused == conventional, "driver did not reuse the buffer name"); + + GLStateManager.glBindVertexArray(vao); + GLStateManager.glDisableVertexAttribArray(position); + assertEquals(generic, driverAttrib(position, GL15.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING), "a pointer set before the delete must not attach to the new buffer"); + } finally { + GLStateManager.glBindVertexArray(vao); + GLStateManager.glDisableVertexAttribArray(position); + GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + GLStateManager.glDeleteVertexArrays(other); + GLStateManager.glDeleteBuffers(generic); + if (reused != 0) GLStateManager.glDeleteBuffers(reused); + } + } + + @Test + void popKeepsCurrentArraysWhenTheSavedArrayBufferWasDeleted() { + final int vbo2 = GLStateManager.glGenBuffers(); + try { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo2); + GLStateManager.glVertexPointer(2, GL11.GL_SHORT, 4, 0L); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.glDeleteBuffers(vbo); + GLStateManager.glPopClientAttrib(); + + assertEquals(2, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_SIZE), "array state is not restored once its array buffer is gone"); + assertEquals(vbo2, GLStateManager.glGetInteger(GL15.GL_ARRAY_BUFFER_BINDING)); + assertEquals(GL13.GL_TEXTURE0, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE)); + } finally { + drainClientAttribStack(); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + GLStateManager.glDeleteBuffers(vbo2); + } + } + + @Test + void clientAttribStackIsNotCompiledIntoDisplayLists() { + final int other = GLStateManager.glGenVertexArrays(); + final int list = GLStateManager.glGenLists(1); + try { + GLStateManager.glNewList(list, GL11.GL_COMPILE); + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glPopClientAttrib(); + GLStateManager.glEndList(); + + GLStateManager.glBindVertexArray(other); + GLStateManager.glCallList(list); + assertEquals(other, GLStateManager.getBoundVAO(), "replaying the list must not rebind the vertex array that was bound while compiling"); + } finally { + drainClientAttribStack(); + GLStateManager.glDeleteLists(list, 1); + GLStateManager.glDeleteVertexArrays(other); + } + } + + @Test + void popRestoresDivisorAndStateWithoutAnAttributeLocation() { + try { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); + GLStateManager.glVertexAttribPointer(5, 1, GL11.GL_FLOAT, false, 4, 0L); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glPopClientAttrib(); + assertEquals(0, driverAttrib(5, GL33.GL_VERTEX_ATTRIB_ARRAY_DIVISOR)); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glVertexAttribDivisor(5, 3); + assertEquals(3, driverAttrib(5, GL33.GL_VERTEX_ATTRIB_ARRAY_DIVISOR)); + GLStateManager.glPopClientAttrib(); + assertEquals(0, driverAttrib(5, GL33.GL_VERTEX_ATTRIB_ARRAY_DIVISOR)); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glEnableClientState(GL14.GL_FOG_COORDINATE_ARRAY); + GLStateManager.glPopClientAttrib(); + assertFalse(GLStateManager.glIsEnabled(GL14.GL_FOG_COORDINATE_ARRAY)); + + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glEnableVertexAttribArray(5); + GLStateManager.glPopClientAttrib(); + assertEquals(GL11.GL_FALSE, driverAttrib(5, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED)); + } finally { + drainClientAttribStack(); + GLStateManager.glDisableVertexAttribArray(5); + GLStateManager.glDisableClientState(GL14.GL_FOG_COORDINATE_ARRAY); + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + } + } + + @Test + void interleavedArraysFromAClientBufferRegisterClientPointers() { + final FloatBuffer data = BufferUtils.createFloatBuffer(64); + final int position = Usage.POSITION.getAttributeLocation(); + try { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + data.position(2); + + GLStateManager.glInterleavedArrays(GL11.GL_C4UB_V3F, 0, data); + final long base = MemoryUtilities.memAddress(data); + assertNotNull(VAOManager.get(position).clientPointer); + assertEquals(base + 4, MemoryUtilities.memAddress(VAOManager.get(position).clientPointer)); + assertEquals(base + 4, MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_VERTEX_ARRAY_POINTER, 4))); + assertEquals(base, MemoryUtilities.memAddress(GLStateManager.glGetPointer(GL11.GL_COLOR_ARRAY_POINTER, 4))); + assertEquals(0, GLStateManager.glGetInteger(GL15.GL_VERTEX_ARRAY_BUFFER_BINDING)); + assertEquals(16, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_STRIDE)); + assertTrue(GLStateManager.ctx().vaos.hasAnyClientSideEnabledAttrib()); + } finally { + for (final int cap : CLIENT_ARRAY_CAPS) { + GLStateManager.glDisableClientState(cap); + } + } + } + + @Test + void enableAndDisableRouteClientArrayCapsToClientState() { + for (final int cap : CLIENT_ARRAY_CAPS) { + GLStateManager.glEnable(cap); + assertTrue(GLStateManager.glIsEnabled(cap), hex(cap)); + GLStateManager.glDisable(cap); + assertFalse(GLStateManager.glIsEnabled(cap), hex(cap)); + } + GLStateManager.glEnable(GL11.GL_VERTEX_ARRAY); + assertTrue(positionArrayEnabled()); + GLStateManager.glDisable(GL11.GL_VERTEX_ARRAY); + } + + @Test + void illegalPointerArgumentsAreIgnored() { + try { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); + GLStateManager.glVertexPointer(3, GL11.GL_FLOAT, 12, 0L); + GLStateManager.glNormalPointer(GL11.GL_FLOAT, 12, 0L); + + GLStateManager.glVertexPointer(1, GL11.GL_FLOAT, 4, 0L); + GLStateManager.glVertexPointer(3, GL11.GL_UNSIGNED_BYTE, 3, 0L); + GLStateManager.glNormalPointer(GL11.GL_UNSIGNED_BYTE, 3, 0L); + assertVertexArray("vertex after illegal calls", 3, GL11.GL_FLOAT, 12, vbo); + assertEquals(GL11.GL_FLOAT, GLStateManager.glGetInteger(GL11.GL_NORMAL_ARRAY_TYPE)); + + GLStateManager.glColorPointer(GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, 4, 0L); + assertEquals(GL12.GL_BGRA, GLStateManager.glGetInteger(GL11.GL_COLOR_ARRAY_SIZE)); + GLStateManager.glColorPointer(GL12.GL_BGRA, GL11.GL_FLOAT, 16, 0L); + assertEquals(GL11.GL_UNSIGNED_BYTE, GLStateManager.glGetInteger(GL11.GL_COLOR_ARRAY_TYPE)); + + GLStateManager.glVertexAttribPointer(VAOManager.MAX_ATTRIBS, 3, GL11.GL_FLOAT, false, 0, 0L); + GLStateManager.glEnableVertexAttribArray(VAOManager.MAX_ATTRIBS); + GLStateManager.glDisableVertexAttribArray(VAOManager.MAX_ATTRIBS); + GLStateManager.glVertexAttribPointer(5, 5, GL11.GL_FLOAT, false, 0, 0L); + assertNull(VAOManager.get(5)); + } finally { + GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + } + } + + @Test + void clientArrayEnablesAreAnsweredFromCache() { + final int vaoB = GLStateManager.glGenVertexArrays(); + try { + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + for (final int cap : CLIENT_ARRAY_CAPS) { + assertFalse(GLStateManager.glIsEnabled(cap), "default isEnabled " + hex(cap)); + assertEnableRoundTrip(cap, true); + } + + GLStateManager.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); + assertFalse(GLStateManager.glIsEnabled(GL11.GL_TEXTURE_COORD_ARRAY), "texcoord array enable is per client texture unit"); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + assertTrue(GLStateManager.glIsEnabled(GL11.GL_TEXTURE_COORD_ARRAY)); + GLStateManager.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY); + + GLStateManager.glEnableClientState(GL11.GL_INDEX_ARRAY); + GLStateManager.glBindVertexArray(vaoB); + assertFalse(GLStateManager.glIsEnabled(GL11.GL_INDEX_ARRAY), "array enables are per vertex array object"); + GLStateManager.glBindVertexArray(vao); + assertTrue(GLStateManager.glIsEnabled(GL11.GL_INDEX_ARRAY)); + GLStateManager.glDisableClientState(GL11.GL_INDEX_ARRAY); + + GLStateManager.glBindVertexArray(vaoB); + GLStateManager.glEnableVertexAttribArray(0); + assertFalse(GLStateManager.glIsEnabled(GL11.GL_VERTEX_ARRAY), "an enabled generic attribute 0 is not GL_VERTEX_ARRAY"); + GLStateManager.glDisableVertexAttribArray(0); + GLStateManager.glBindVertexArray(vao); + } finally { + GLStateManager.glBindVertexArray(vaoB); + GLStateManager.glDisableVertexAttribArray(0); + GLStateManager.glBindVertexArray(vao); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + for (final int cap : CLIENT_ARRAY_CAPS) { + GLStateManager.glDisableClientState(cap); + } + GLStateManager.glDeleteVertexArrays(vaoB); + } + } +} diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_CoreRemovedCaps_GLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_CoreRemovedCaps_GLTest.java index 0f70988d6..969d8e516 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_CoreRemovedCaps_GLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_CoreRemovedCaps_GLTest.java @@ -76,7 +76,6 @@ public class GLSM_CoreRemovedCaps_GLTest { assertFalse(GLStateManager.glIsEnabled(cap)); } finally { GLStateManager.glDisable(cap); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} } } @@ -91,7 +90,6 @@ public class GLSM_CoreRemovedCaps_GLTest { assertFalse(GLStateManager.glIsEnabled(cap)); } finally { GLStateManager.glDisable(cap); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} } } @@ -105,7 +103,6 @@ public class GLSM_CoreRemovedCaps_GLTest { assertSecondaryColor(0.25F, 0.5F, 0.75F); } finally { GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} } } @@ -121,7 +118,6 @@ public class GLSM_CoreRemovedCaps_GLTest { assertSecondaryColor(0.125F, 0.25F, 0.375F); } finally { GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} } } @@ -135,7 +131,6 @@ public class GLSM_CoreRemovedCaps_GLTest { assertFalse(GLStateManager.glIsEnabled(GL14.GL_COLOR_SUM)); } finally { GLStateManager.glDisable(GL14.GL_COLOR_SUM); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} } } @@ -156,7 +151,6 @@ public class GLSM_CoreRemovedCaps_GLTest { } finally { GLStateManager.glDeleteLists(list, 1); GLStateManager.glSecondaryColor3f(0.0F, 0.0F, 0.0F); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} } } @@ -176,7 +170,6 @@ public class GLSM_CoreRemovedCaps_GLTest { } finally { GLStateManager.glDeleteLists(list, 1); GLStateManager.glDisable(GL11.GL_LINE_STIPPLE); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} } } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DeferredTextureDelete_GLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DeferredTextureDelete_GLTest.java new file mode 100644 index 000000000..fe159ce24 --- /dev/null +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DeferredTextureDelete_GLTest.java @@ -0,0 +1,62 @@ +package com.gtnewhorizons.angelica.glsm; + +import org.junit.jupiter.api.Test; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; +import org.lwjgl.opengl.GL30; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +@GLCoreTest +public class GLSM_DeferredTextureDelete_GLTest { + + private static int immutableTexture() { + assumeTrue(GLStateManager.capabilities.OpenGL42, "no glTexStorage2D on this context"); + final int tex = GLStateManager.glGenTextures(); + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex); + GLStateManager.glTexStorage2D(GL11.GL_TEXTURE_2D, 1, GL30.GL_R8, 4, 4); + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0); + return tex; + } + + @Test + void deletingAnImmutableTextureRaisesNoError() { + final int tex = immutableTexture(); + + GLStateManager.glDeleteTextures(tex); + + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + } + + @Test + void deletingARescuedImmutableTextureRaisesNoError() { + final int tex = immutableTexture(); + GLStateManager.glDeleteTextures(tex); + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex); + + GLStateManager.glDeleteTextures(tex); + + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + } + + @Test + void deletingA2DTextureWhileA3DTextureIsBoundLeavesNothingBoundTo2D() { + final int t2d = GLStateManager.glGenTextures(); + final int t3d = GLStateManager.glGenTextures(); + try { + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, t2d); + GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, t3d); + + GLStateManager.glDeleteTextures(t2d); + + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); + assertEquals(0, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "the deleted texture must not stay bound"); + assertEquals(t3d, GL11.glGetInteger(GL12.GL_TEXTURE_BINDING_3D)); + } finally { + GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, 0); + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0); + GLStateManager.glDeleteTextures(t3d); + } + } +} diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DirtyTracking_UnitTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DirtyTracking_UnitTest.java index 914444ffc..dd6363156 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DirtyTracking_UnitTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DirtyTracking_UnitTest.java @@ -6,18 +6,12 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.lwjgl.opengl.GL11; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.floats; import static org.junit.jupiter.api.Assertions.assertEquals; @GLCoreTest public class GLSM_DirtyTracking_UnitTest { - static final FloatBuffer f4b = ByteBuffer.allocateDirect(4 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer(); - static final FloatBuffer f3b = ByteBuffer.allocateDirect(3 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer(); - @BeforeEach void pushTrackedState() { GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT | GL11.GL_FOG_BIT | GL11.GL_CURRENT_BIT); @@ -30,38 +24,31 @@ public class GLSM_DirtyTracking_UnitTest { @Test void materialBumpsOnlyOnChange() { - newf4b(0.1F, 0.2F, 0.3F, 0.4F); - GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b); + GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, floats(0.1F, 0.2F, 0.3F, 0.4F)); final int gen = GLStateManager.getLightingGeneration(); - newf4b(0.1F, 0.2F, 0.3F, 0.4F); - GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b); + GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, floats(0.1F, 0.2F, 0.3F, 0.4F)); assertEquals(gen, GLStateManager.getLightingGeneration(), "identical material must not bump lightingGeneration"); - newf4b(0.9F, 0.2F, 0.3F, 0.4F); - GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b); + GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, floats(0.9F, 0.2F, 0.3F, 0.4F)); assertEquals(gen + 1, GLStateManager.getLightingGeneration(), "changed material must bump lightingGeneration once"); } @Test void lightModelBumpsOnlyOnChange() { - newf4b(0.5F, 0.5F, 0.5F, 1.0F); - GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, f4b); + GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, floats(0.5F, 0.5F, 0.5F, 1.0F)); final int gen = GLStateManager.getLightingGeneration(); - newf4b(0.5F, 0.5F, 0.5F, 1.0F); - GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, f4b); + GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, floats(0.5F, 0.5F, 0.5F, 1.0F)); assertEquals(gen, GLStateManager.getLightingGeneration(), "identical light model ambient must not bump"); } @Test void lightColorBumpsOnlyOnChange() { - newf4b(0.25F, 0.5F, 0.75F, 1.0F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, floats(0.25F, 0.5F, 0.75F, 1.0F)); final int gen = GLStateManager.getLightingGeneration(); - newf4b(0.25F, 0.5F, 0.75F, 1.0F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, floats(0.25F, 0.5F, 0.75F, 1.0F)); assertEquals(gen, GLStateManager.getLightingGeneration(), "identical light diffuse must not bump"); } @@ -72,17 +59,14 @@ public class GLSM_DirtyTracking_UnitTest { GLStateManager.glLoadIdentity(); GLStateManager.glTranslatef(1.0F, 2.0F, 3.0F); - newf4b(0.1F, 0.2F, 0.3F, 1.0F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.1F, 0.2F, 0.3F, 1.0F)); final int gen = GLStateManager.getLightingGeneration(); - newf4b(0.1F, 0.2F, 0.3F, 1.0F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.1F, 0.2F, 0.3F, 1.0F)); assertEquals(gen, GLStateManager.getLightingGeneration(), "same position under same modelview must not bump"); GLStateManager.glTranslatef(5.0F, 0.0F, 0.0F); - newf4b(0.1F, 0.2F, 0.3F, 1.0F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.1F, 0.2F, 0.3F, 1.0F)); assertEquals(gen + 1, GLStateManager.getLightingGeneration(), "same raw position under changed modelview must bump"); GLStateManager.glPopMatrix(); @@ -128,18 +112,15 @@ public class GLSM_DirtyTracking_UnitTest { GLStateManager.glPushMatrix(); GLStateManager.glLoadIdentity(); - newf3b(0.0F, 0.0F, -1.0F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f3b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.0F, 0.0F, -1.0F)); final int gen = GLStateManager.getLightingGeneration(); GLStateManager.glTranslatef(5.0F, 0.0F, 0.0F); - newf3b(0.0F, 0.0F, -1.0F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f3b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.0F, 0.0F, -1.0F)); assertEquals(gen, GLStateManager.getLightingGeneration(), "spot direction under translation-only must not bump"); GLStateManager.glRotatef(90.0F, 0.0F, 1.0F, 0.0F); - newf3b(0.0F, 0.0F, -1.0F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f3b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.0F, 0.0F, -1.0F)); assertEquals(gen + 1, GLStateManager.getLightingGeneration(), "same raw spot direction under changed 3x3 must bump"); GLStateManager.glPopMatrix(); @@ -158,16 +139,4 @@ public class GLSM_DirtyTracking_UnitTest { assertEquals(0.0F, diffuse.y, "green channel must be 0"); assertEquals(1.0F, diffuse.z, "blue channel must be 1"); } - - static void newf4b(float x, float y, float z, float w) { - f4b.clear(); - f4b.put(x).put(y).put(z).put(w); - f4b.flip(); - } - - static void newf3b(float x, float y, float z) { - f3b.clear(); - f3b.put(x).put(y).put(z); - f3b.flip(); - } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_FormatOptimization_Test.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_FormatOptimization_Test.java index 7b37276e8..1c5d6c5fb 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_FormatOptimization_Test.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_FormatOptimization_Test.java @@ -2,6 +2,8 @@ package com.gtnewhorizons.angelica.glsm; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.lwjgl.opengl.GL11; import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState; @@ -28,106 +30,26 @@ class GLSM_DisplayList_FormatOptimization_Test { GLStateManager.glNormal3f(0.0f, 0.0f, 1.0f); } - @Test - void testPositionOnlyFormat() { - // Create a display list with only position data (no color, texture, brightness, or normals) - // Use GLStateManager for all calls to ensure they go through GLSM (test env doesn't redirect GL11) + private static final float[][] CORNERS = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; + private static final float[][] COLORS = {{1, 1, 1, 1}, {1, 0, 0, 1}, {0, 1, 0, 1}, {0, 0, 1, 1}}; + + @ParameterizedTest(name = "normals={0}") + @ValueSource(booleans = {false, true}) + void colorTextureFormatLeavesLastColorCurrent(boolean normals) { displayList = GL11.glGenLists(1); GLStateManager.glNewList(displayList, GL11.GL_COMPILE); GLStateManager.glBegin(GL11.GL_QUADS); - GLStateManager.glVertex3f(0, 0, 0); - GLStateManager.glVertex3f(1, 0, 0); - GLStateManager.glVertex3f(1, 1, 0); - GLStateManager.glVertex3f(0, 1, 0); + for (int i = 0; i < 4; i++) { + if (normals) GLStateManager.glNormal3f(0, 0, 1); + GLStateManager.glColor4f(COLORS[i][0], COLORS[i][1], COLORS[i][2], COLORS[i][3]); + GLStateManager.glTexCoord2f(CORNERS[i][0], CORNERS[i][1]); + GLStateManager.glVertex3f(CORNERS[i][0], CORNERS[i][1], 0); + } GLStateManager.glEnd(); GLStateManager.glEndList(); - // Call the list to ensure it doesn't crash (verifies compilation succeeded) - GLStateManager.glCallList(displayList); - } - - @Test - void testPositionTextureFormat() { - // Create a display list with position and texture coordinates - displayList = GL11.glGenLists(1); - GLStateManager.glNewList(displayList, GL11.GL_COMPILE); - - GLStateManager.glBegin(GL11.GL_QUADS); - GLStateManager.glTexCoord2f(0, 0); - GLStateManager.glVertex3f(0, 0, 0); - GLStateManager.glTexCoord2f(1, 0); - GLStateManager.glVertex3f(1, 0, 0); - GLStateManager.glTexCoord2f(1, 1); - GLStateManager.glVertex3f(1, 1, 0); - GLStateManager.glTexCoord2f(0, 1); - GLStateManager.glVertex3f(0, 1, 0); - GLStateManager.glEnd(); - - GLStateManager.glEndList(); - - // Call the list to ensure it doesn't crash (verifies compilation succeeded) - GLStateManager.glCallList(displayList); - } - - @Test - void testPositionColorTextureFormat() { - // Create a display list with position, color, and texture - displayList = GL11.glGenLists(1); - GLStateManager.glNewList(displayList, GL11.GL_COMPILE); - - GLStateManager.glBegin(GL11.GL_QUADS); - GLStateManager.glColor4f(1, 1, 1, 1); - GLStateManager.glTexCoord2f(0, 0); - GLStateManager.glVertex3f(0, 0, 0); - GLStateManager.glColor4f(1, 0, 0, 1); - GLStateManager.glTexCoord2f(1, 0); - GLStateManager.glVertex3f(1, 0, 0); - GLStateManager.glColor4f(0, 1, 0, 1); - GLStateManager.glTexCoord2f(1, 1); - GLStateManager.glVertex3f(1, 1, 0); - GLStateManager.glColor4f(0, 0, 1, 1); - GLStateManager.glTexCoord2f(0, 1); - GLStateManager.glVertex3f(0, 1, 0); - GLStateManager.glEnd(); - - GLStateManager.glEndList(); - - // Call the list and verify color is restored to last value (blue) - GLStateManager.glCallList(displayList); - verifyState(GL11.GL_CURRENT_COLOR, new float[]{0f, 0f, 1f, 1f}, - "After playback, color should be blue (last color in immediate mode block)"); - } - - @Test - void testFullFormat() { - // Create a display list with all attributes: position, color, texture, and normal - displayList = GL11.glGenLists(1); - GLStateManager.glNewList(displayList, GL11.GL_COMPILE); - - GLStateManager.glBegin(GL11.GL_QUADS); - GLStateManager.glNormal3f(0, 0, 1); - GLStateManager.glColor4f(1, 1, 1, 1); - GLStateManager.glTexCoord2f(0, 0); - GLStateManager.glVertex3f(0, 0, 0); - GLStateManager.glNormal3f(0, 0, 1); - GLStateManager.glColor4f(1, 0, 0, 1); - GLStateManager.glTexCoord2f(1, 0); - GLStateManager.glVertex3f(1, 0, 0); - GLStateManager.glNormal3f(0, 0, 1); - GLStateManager.glColor4f(0, 1, 0, 1); - GLStateManager.glTexCoord2f(1, 1); - GLStateManager.glVertex3f(1, 1, 0); - GLStateManager.glNormal3f(0, 0, 1); - GLStateManager.glColor4f(0, 0, 1, 1); - GLStateManager.glTexCoord2f(0, 1); - GLStateManager.glVertex3f(0, 1, 0); - GLStateManager.glEnd(); - - GLStateManager.glEndList(); - - // Call the list and verify color is restored to last value (blue) GLStateManager.glCallList(displayList); verifyState(GL11.GL_CURRENT_COLOR, new float[]{0f, 0f, 1f, 1f}, "After playback, color should be blue (last color in immediate mode block)"); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_PixelStore_Test.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_PixelStore_Test.java index 373065ff5..dffb7a157 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_PixelStore_Test.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_PixelStore_Test.java @@ -2,6 +2,8 @@ package com.gtnewhorizons.angelica.glsm; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.lwjgl.opengl.GL11; import java.nio.ByteBuffer; @@ -15,6 +17,23 @@ public class GLSM_DisplayList_PixelStore_Test { private static final int SRC = 8; private static final int SUB = 4; + enum Upload { + TEX_SUB_IMAGE_2D { + @Override + void run(ByteBuffer src) { + GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src); + } + }, + TEX_IMAGE_2D { + @Override + void run(ByteBuffer src) { + GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SUB, SUB, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src); + } + }; + + abstract void run(ByteBuffer src); + } + @AfterEach void resetUnpackState() { GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4); @@ -96,8 +115,9 @@ public class GLSM_DisplayList_PixelStore_Test { GLStateManager.glDeleteLists(list, 1); } - @Test - void stridedTexSubImage2DCompile() { + @ParameterizedTest + @EnumSource(Upload.class) + void stridedUploadCompile(Upload upload) { final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder())); final ByteBuffer src = makeSource(SRC); @@ -105,7 +125,7 @@ public class GLSM_DisplayList_PixelStore_Test { final int list = GLStateManager.glGenLists(1); GLStateManager.glNewList(list, GL11.GL_COMPILE); - GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src); + upload.run(src); GLStateManager.glEndList(); assertEquals(0, red(readback(SUB), SUB, 0, 0), "GL_COMPILE must not execute the upload"); @@ -146,58 +166,15 @@ public class GLSM_DisplayList_PixelStore_Test { GLStateManager.glDeleteTextures(tex); } - @Test - void stridedTexImage2DCompile() { - final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder())); - final ByteBuffer src = makeSource(SRC); - - setStridedUnpack(); - - final int list = GLStateManager.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE); - GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SUB, SUB, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src); - GLStateManager.glEndList(); - - setUnpack(0, 0, 0); - - GLStateManager.glCallList(list); - assertStridedSubImage(readback(SUB)); - assertUnpack(0, 0, 0); - - GLStateManager.glDeleteLists(list, 1); - GLStateManager.glDeleteTextures(tex); - } - - @Test - void defaultRecordedTexSubImage2DReplaysWithDefaults() { + @ParameterizedTest + @EnumSource(Upload.class) + void defaultRecordedUploadReplaysWithDefaults(Upload upload) { final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder())); final ByteBuffer src = makeSource(SUB); final int list = GLStateManager.glGenLists(1); GLStateManager.glNewList(list, GL11.GL_COMPILE); - GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, SUB, SUB, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src); - GLStateManager.glEndList(); - - GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); - setStridedUnpack(); - - GLStateManager.glCallList(list); - assertTightSubImage(readback(SUB)); - assertEquals(1, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT), "live state must survive replay"); - assertUnpack(SRC, 1, 2); - - GLStateManager.glDeleteLists(list, 1); - GLStateManager.glDeleteTextures(tex); - } - - @Test - void defaultRecordedTexImage2DReplaysWithDefaults() { - final int tex = makeTexture(SUB, ByteBuffer.allocateDirect(SUB * SUB * 4).order(ByteOrder.nativeOrder())); - final ByteBuffer src = makeSource(SUB); - - final int list = GLStateManager.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE); - GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SUB, SUB, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, src); + upload.run(src); GLStateManager.glEndList(); GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_RawTessellatorTransform_Test.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_RawTessellatorTransform_Test.java index 7f7225cc3..31f45a364 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_RawTessellatorTransform_Test.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_RawTessellatorTransform_Test.java @@ -1,14 +1,14 @@ package com.gtnewhorizons.angelica.glsm; -import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager; import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList; import com.gtnewhorizons.angelica.glsm.recording.GLCommand; import it.unimi.dsi.fastutil.ints.IntList; -import net.minecraft.client.renderer.Tessellator; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.lwjgl.opengl.GL11; +import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.immediateQuad; +import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.rawQuad; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -27,34 +27,6 @@ class GLSM_DisplayList_RawTessellatorTransform_Test { GLStateManager.glLoadIdentity(); } - private static void rawQuad(float size) { - final Tessellator tess = Tessellator.instance; - final float[][] corners = { { 0.0f, 0.0f }, { size, 0.0f }, { size, size }, { 0.0f, size } }; - tess.rawBuffer = new int[4 * 8]; - for (int i = 0; i < 4; i++) { - final int base = i * 8; - tess.rawBuffer[base] = Float.floatToRawIntBits(corners[i][0]); - tess.rawBuffer[base + 1] = Float.floatToRawIntBits(corners[i][1]); - tess.rawBuffer[base + 3] = Float.floatToRawIntBits(i == 1 || i == 2 ? 1.0f : 0.0f); - tess.rawBuffer[base + 4] = Float.floatToRawIntBits(i >= 2 ? 1.0f : 0.0f); - } - tess.isDrawing = true; - tess.drawMode = GL11.GL_QUADS; - tess.hasTexture = true; - tess.rawBufferIndex = 4 * 8; - tess.vertexCount = 4; - TessellatorManager.interceptDraw(tess); - } - - private static void immediateQuad(float size) { - GLStateManager.glBegin(GL11.GL_QUADS); - GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f); - GLStateManager.glVertex3f(size, 0.0f, 0.0f); - GLStateManager.glVertex3f(size, size, 0.0f); - GLStateManager.glVertex3f(0.0f, size, 0.0f); - GLStateManager.glEnd(); - } - @Test void pendingTransformIsEmittedBeforeARawDraw() { testList = GLStateManager.glGenLists(1); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_TransformCollapsing_Test.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_TransformCollapsing_Test.java index 907a85e4f..58550cafa 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_TransformCollapsing_Test.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_TransformCollapsing_Test.java @@ -1,19 +1,25 @@ package com.gtnewhorizons.angelica.glsm; -import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager; import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList; import com.gtnewhorizons.angelica.glsm.recording.GLCommand; -import net.minecraft.client.renderer.Tessellator; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.IntList; import org.joml.Matrix4f; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import java.nio.FloatBuffer; +import java.util.stream.Stream; +import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.assertMatrixEquals; +import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.immediateQuad; +import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.modelviewAfterCall; +import static com.gtnewhorizons.angelica.glsm.DisplayListTestHelper.rawQuad; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -70,12 +76,7 @@ class GLSM_DisplayList_TransformCollapsing_Test { "No individual transform commands should remain in optimized list"); // Verify behavioral correctness: matrix state should match expected - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf); + final FloatBuffer actualBuf = modelviewAfterCall(testList); Matrix4f expected = new Matrix4f() .translate(1.0f, 2.0f, 3.0f) @@ -84,15 +85,7 @@ class GLSM_DisplayList_TransformCollapsing_Test { .translate(-0.5f, -0.5f, -0.5f) .rotate((float) Math.toRadians(30.0f), 1.0f, 0.0f, 0.0f); - FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16); - expected.get(expectedBuf); - - for (int i = 0; i < 16; i++) { - assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f, - String.format("Matrix[%d] mismatch after collapsed transforms", i)); - } - - GLStateManager.glPopMatrix(); + assertMatrixEquals(expected, actualBuf, "mismatch after collapsed transforms"); } @Test @@ -121,12 +114,7 @@ class GLSM_DisplayList_TransformCollapsing_Test { assertEquals(1, popCount, "Should have exactly 1 PopMatrix"); // Verify behavioral correctness - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf); + final FloatBuffer actualBuf = modelviewAfterCall(testList); // Expected: translate(10,0,0) then translate(0,3,0) = translate(10,3,0) // The inner push/pop isolates the (5,0,0) translate and rotation @@ -134,15 +122,7 @@ class GLSM_DisplayList_TransformCollapsing_Test { .translate(10.0f, 0.0f, 0.0f) .translate(0.0f, 3.0f, 0.0f); - FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16); - expected.get(expectedBuf); - - for (int i = 0; i < 16; i++) { - assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f, - String.format("Matrix[%d] mismatch - push/pop should isolate inner transforms", i)); - } - - GLStateManager.glPopMatrix(); + assertMatrixEquals(expected, actualBuf, "mismatch - push/pop should isolate inner transforms"); } @Test @@ -164,12 +144,7 @@ class GLSM_DisplayList_TransformCollapsing_Test { GLStateManager.glEndList(); // Verify behavioral correctness - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf); + final FloatBuffer actualBuf = modelviewAfterCall(testList); // Final state: translate(1,0,0) + translate(5,0,0) = translate(6,0,0) // Level 1 and 2 transforms are isolated by push/pop @@ -182,8 +157,6 @@ class GLSM_DisplayList_TransformCollapsing_Test { assertEquals(expectedBuf.get(12), actualBuf.get(12), 0.0001f, "X translation should be 6.0 (1+5, inner transforms isolated)"); - - GLStateManager.glPopMatrix(); // Balance the push from line 185 } @Test @@ -219,12 +192,7 @@ class GLSM_DisplayList_TransformCollapsing_Test { "MultMatrix should be emitted BEFORE CallList (transform flush at barrier)"); // Verify behavioral correctness - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf); + final FloatBuffer actualBuf = modelviewAfterCall(testList); // Build expected: translate(10,0,0) * rotate(90,Y) * translate(100,0,0) * translate(1,0,0) // After 90-degree Y rotation, X axis points into -Z, so nested translate goes along -Z @@ -234,15 +202,7 @@ class GLSM_DisplayList_TransformCollapsing_Test { .translate(100.0f, 0.0f, 0.0f) .translate(1.0f, 0.0f, 0.0f); - FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16); - expected.get(expectedBuf); - - for (int i = 0; i < 16; i++) { - assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f, - String.format("Matrix[%d] mismatch - nested list should see parent's transformed state", i)); - } - - GLStateManager.glPopMatrix(); + assertMatrixEquals(expected, actualBuf, "mismatch - nested list should see parent's transformed state"); } @Test @@ -271,63 +231,33 @@ class GLSM_DisplayList_TransformCollapsing_Test { "Identity-equivalent transforms should not emit MultMatrix"); } - @Test - void testTransformBetweenDrawsCreatesSeparateBatches() { - // This test verifies that draws with different transforms are not batched together - // Note: Without actual Tessellator integration, we can only verify command structure - - testList = GL11.glGenLists(1); - GLStateManager.glNewList(testList, GL11.GL_COMPILE); - - // Simulate: Transform A, Draw, Transform B, Draw - // The draws should NOT be in the same batch - GLStateManager.glTranslatef(1.0f, 0.0f, 0.0f); - // (Would have a draw here in real usage) - GLStateManager.glTranslatef(2.0f, 0.0f, 0.0f); - // (Would have another draw here) - - GLStateManager.glEndList(); - - // Verify the transforms are accumulated correctly - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf); - - // Total translation should be 3.0 (1+2) - assertEquals(3.0f, actualBuf.get(12), 0.0001f, - "Consecutive translates should accumulate"); - - GLStateManager.glPopMatrix(); + static Stream discardedByLoadIdentity() { + return Stream.of( + Arguments.of("accumulated translate", (Runnable) () -> GLStateManager.glTranslatef(100.0f, 100.0f, 100.0f)), + Arguments.of("loaded matrix", (Runnable) () -> { + final FloatBuffer loadBuf = BufferUtils.createFloatBuffer(16); + new Matrix4f().translate(50.0f, 60.0f, 70.0f).rotate((float) Math.toRadians(45.0f), 1.0f, 1.0f, 0.0f).get(loadBuf); + GLStateManager.glLoadMatrix(loadBuf); + })); } - @Test - void testLoadIdentityResetsAccumulation() { - // LoadIdentity should reset accumulated transforms + @ParameterizedTest(name = "{0}") + @MethodSource("discardedByLoadIdentity") + void testLoadIdentityDiscardsEarlierTransforms(String name, Runnable earlier) { testList = GL11.glGenLists(1); GLStateManager.glNewList(testList, GL11.GL_COMPILE); - GLStateManager.glTranslatef(100.0f, 100.0f, 100.0f); // Big translate - GLStateManager.glLoadIdentity(); // Reset - GLStateManager.glTranslatef(1.0f, 2.0f, 3.0f); // New translate + earlier.run(); + GLStateManager.glLoadIdentity(); + GLStateManager.glTranslatef(1.0f, 2.0f, 3.0f); GLStateManager.glEndList(); - // Verify: final state should only have the second translate - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf); + final FloatBuffer actualBuf = modelviewAfterCall(testList); assertEquals(1.0f, actualBuf.get(12), 0.0001f, "X should be 1.0 (LoadIdentity reset)"); assertEquals(2.0f, actualBuf.get(13), 0.0001f, "Y should be 2.0 (LoadIdentity reset)"); assertEquals(3.0f, actualBuf.get(14), 0.0001f, "Z should be 3.0 (LoadIdentity reset)"); - - GLStateManager.glPopMatrix(); } @Test @@ -359,121 +289,14 @@ class GLSM_DisplayList_TransformCollapsing_Test { "Translate + MultMatrix + Translate should collapse to 1 MultMatrix"); // Verify behavioral correctness - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf); + final FloatBuffer actualBuf = modelviewAfterCall(testList); Matrix4f expected = new Matrix4f() .translate(1.0f, 0.0f, 0.0f) .rotate((float) Math.toRadians(90.0f), 0.0f, 1.0f, 0.0f) .translate(2.0f, 0.0f, 0.0f); - FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16); - expected.get(expectedBuf); - - for (int i = 0; i < 16; i++) { - assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f, - String.format("Matrix[%d] mismatch for MultMatrix accumulation", i)); - } - - GLStateManager.glPopMatrix(); - } - - // ==================== Batcher Flush Ordering Tests ==================== - - @Test - void testDrawsBeforePushMatrixAreEmittedBeforePush() { - // This test verifies that draws occurring before a PushMatrix are flushed - // before the PushMatrix command, not delayed until later. - // This was a bug where the batcher wasn't flushed at PushMatrix. - - // Create a list with: Transform, (implicit draw would go here), PushMatrix, Transform, PopMatrix - testList = GL11.glGenLists(1); - GLStateManager.glNewList(testList, GL11.GL_COMPILE); - - // Outer transform - GLStateManager.glTranslatef(1.0f, 0.0f, 0.0f); - // Note: If there was a draw here, it should be emitted BEFORE PushMatrix - - GLStateManager.glPushMatrix(); - GLStateManager.glTranslatef(5.0f, 0.0f, 0.0f); // Inner transform - GLStateManager.glPopMatrix(); - - // Final transform after pop - GLStateManager.glTranslatef(2.0f, 0.0f, 0.0f); - - GLStateManager.glEndList(); - - // Verify the command structure - CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList); - assertNotNull(compiled, "Display list should be compiled"); - - IntList opcodes = compiled.getCommandOpcodes(); - - // Find indices of key commands - int pushIndex = opcodes.indexOf(GLCommand.PUSH_MATRIX); - int popIndex = opcodes.indexOf(GLCommand.POP_MATRIX); - - // Count MultMatrix commands before first PushMatrix - int multMatrixBeforePushCount = 0; - for (int i = 0; i < opcodes.size() && i < pushIndex; i++) { - if (opcodes.get(i) == GLCommand.TRANSLATE) { - multMatrixBeforePushCount++; - } - } - - assertTrue(pushIndex >= 0, "Should have PushMatrix command"); - assertTrue(popIndex >= 0, "Should have PopMatrix command"); - assertTrue(pushIndex < popIndex, "PushMatrix should come before PopMatrix"); - - // The outer translate(1,0,0) should be emitted as MultMatrix before PushMatrix - assertTrue(multMatrixBeforePushCount >= 1, - "Transform before PushMatrix should be emitted before the Push"); - - // Verify behavioral correctness: final state should be translate(1,0,0) + translate(2,0,0) = translate(3,0,0) - // The inner translate(5,0,0) is isolated by push/pop - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf); - - assertEquals(3.0f, actualBuf.get(12), 0.0001f, - "X translation should be 3.0 (1+2, inner transform isolated by push/pop)"); - - GLStateManager.glPopMatrix(); - } - - @Test - void testDrawsBeforePopMatrixAreEmittedBeforePop() { - // Verify draws inside a push/pop block are emitted before the pop - testList = GL11.glGenLists(1); - GLStateManager.glNewList(testList, GL11.GL_COMPILE); - - GLStateManager.glPushMatrix(); - GLStateManager.glTranslatef(10.0f, 0.0f, 0.0f); - // Draws here should be emitted before PopMatrix - GLStateManager.glPopMatrix(); - - GLStateManager.glEndList(); - - CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList); - IntList opcodes = compiled.getCommandOpcodes(); - - int pushIndex = opcodes.indexOf(GLCommand.PUSH_MATRIX); - int popIndex = opcodes.indexOf(GLCommand.POP_MATRIX); - - assertTrue(pushIndex >= 0, "Should have PushMatrix"); - assertTrue(popIndex >= 0, "Should have PopMatrix"); - assertTrue(pushIndex < popIndex, "Push should come before Pop"); - - // The inner translate should be emitted between push and pop (or collapsed to residual) - // At minimum, the structure should be valid - assertTrue(popIndex > pushIndex, "PopMatrix should follow PushMatrix"); + assertMatrixEquals(expected, actualBuf, "mismatch for MultMatrix accumulation"); } // ==================== LoadMatrix Tests ==================== @@ -509,22 +332,9 @@ class GLSM_DisplayList_TransformCollapsing_Test { "Should have exactly 1 LoadMatrix command (can't be absorbed)"); // Verify behavioral correctness: matrix should equal the loaded matrix - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); + final FloatBuffer actualBuf = modelviewAfterCall(testList); - FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf); - - FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16); - loadedMatrix.get(expectedBuf); - - for (int i = 0; i < 16; i++) { - assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f, - String.format("Matrix[%d] should match loaded matrix (not multiplied)", i)); - } - - GLStateManager.glPopMatrix(); + assertMatrixEquals(loadedMatrix, actualBuf, "should match loaded matrix (not multiplied)"); } @Test @@ -547,62 +357,11 @@ class GLSM_DisplayList_TransformCollapsing_Test { GLStateManager.glEndList(); // Verify behavioral correctness - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf); + final FloatBuffer actualBuf = modelviewAfterCall(testList); // Expected: loadedMatrix * translate(10,0,0) Matrix4f expected = new Matrix4f(loadedMatrix).translate(10.0f, 0.0f, 0.0f); - FloatBuffer expectedBuf = BufferUtils.createFloatBuffer(16); - expected.get(expectedBuf); - - for (int i = 0; i < 16; i++) { - assertEquals(expectedBuf.get(i), actualBuf.get(i), 0.0001f, - String.format("Matrix[%d] - transform after LoadMatrix should be relative to loaded matrix", i)); - } - - GLStateManager.glPopMatrix(); - } - - @Test - void testLoadMatrixThenLoadIdentity() { - // LoadMatrix followed by LoadIdentity should result in identity - testList = GL11.glGenLists(1); - GLStateManager.glNewList(testList, GL11.GL_COMPILE); - - // Load a complex matrix - Matrix4f loadedMatrix = new Matrix4f() - .translate(50.0f, 60.0f, 70.0f) - .rotate((float) Math.toRadians(45.0f), 1.0f, 1.0f, 0.0f); - FloatBuffer loadBuf = BufferUtils.createFloatBuffer(16); - loadedMatrix.get(loadBuf); - loadBuf.rewind(); - GLStateManager.glLoadMatrix(loadBuf); - - // Then reset to identity - GLStateManager.glLoadIdentity(); - - // Then apply a simple translate - GLStateManager.glTranslatef(1.0f, 2.0f, 3.0f); - - GLStateManager.glEndList(); - - // Verify: final state should just be translate(1,2,3) - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf); - - assertEquals(1.0f, actualBuf.get(12), 0.0001f, "X should be 1.0"); - assertEquals(2.0f, actualBuf.get(13), 0.0001f, "Y should be 2.0"); - assertEquals(3.0f, actualBuf.get(14), 0.0001f, "Z should be 3.0"); - - GLStateManager.glPopMatrix(); + assertMatrixEquals(expected, actualBuf, "transform after LoadMatrix should be relative to loaded matrix"); } @Test @@ -628,17 +387,10 @@ class GLSM_DisplayList_TransformCollapsing_Test { GLStateManager.glEndList(); // Verify: final state should be matrix2 - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer actualBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, actualBuf); + final FloatBuffer actualBuf = modelviewAfterCall(testList); assertEquals(0.0f, actualBuf.get(12), 0.0001f, "X should be 0.0 (from second LoadMatrix)"); assertEquals(20.0f, actualBuf.get(13), 0.0001f, "Y should be 20.0 (from second LoadMatrix)"); - - GLStateManager.glPopMatrix(); } // ==================== Immediate Mode with Transforms Tests ==================== @@ -652,12 +404,7 @@ class GLSM_DisplayList_TransformCollapsing_Test { GLStateManager.glPushMatrix(); GLStateManager.glScalef(0.25f, 0.25f, 0.25f); - GLStateManager.glBegin(GL11.GL_QUADS); - GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f); - GLStateManager.glVertex3f(2.0f, 0.0f, 0.0f); - GLStateManager.glVertex3f(2.0f, 2.0f, 0.0f); - GLStateManager.glVertex3f(0.0f, 2.0f, 0.0f); - GLStateManager.glEnd(); + immediateQuad(2.0f); GLStateManager.glPopMatrix(); GLStateManager.glEndList(); @@ -683,12 +430,7 @@ class GLSM_DisplayList_TransformCollapsing_Test { GLStateManager.glTranslatef(100.0f, 50.0f, 0.0f); GLStateManager.glScalef(0.5f, 0.5f, 1.0f); - GLStateManager.glBegin(GL11.GL_QUADS); - GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f); - GLStateManager.glVertex3f(16.0f, 0.0f, 0.0f); - GLStateManager.glVertex3f(16.0f, 16.0f, 0.0f); - GLStateManager.glVertex3f(0.0f, 16.0f, 0.0f); - GLStateManager.glEnd(); + immediateQuad(16.0f); GLStateManager.glEndList(); @@ -700,20 +442,13 @@ class GLSM_DisplayList_TransformCollapsing_Test { assertEquals(0, counts.getOrDefault(GLCommand.SCALE, 0), "No individual Scale"); // Verify matrix values after playback - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matrixBuf); + final FloatBuffer matrixBuf = modelviewAfterCall(testList); assertEquals(0.5f, matrixBuf.get(0), 0.0001f, "Scale X"); assertEquals(0.5f, matrixBuf.get(5), 0.0001f, "Scale Y"); assertEquals(1.0f, matrixBuf.get(10), 0.0001f, "Scale Z"); assertEquals(100.0f, matrixBuf.get(12), 0.0001f, "Translate X"); assertEquals(50.0f, matrixBuf.get(13), 0.0001f, "Translate Y"); - - GLStateManager.glPopMatrix(); } // ==================== Cross-Mode Transform Collapsing Tests ==================== @@ -806,12 +541,7 @@ class GLSM_DisplayList_TransformCollapsing_Test { GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0); GLStateManager.glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - GLStateManager.glBegin(GL11.GL_QUADS); - GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f); - GLStateManager.glVertex3f(1.0f, 0.0f, 0.0f); - GLStateManager.glVertex3f(1.0f, 1.0f, 0.0f); - GLStateManager.glVertex3f(0.0f, 1.0f, 0.0f); - GLStateManager.glEnd(); + immediateQuad(1.0f); GLStateManager.glEndList(); @@ -826,16 +556,9 @@ class GLSM_DisplayList_TransformCollapsing_Test { assertTrue(opcodes.indexOf(GLCommand.MULT_MATRIX) > opcodes.indexOf(GLCommand.DRAW_RANGE), "MultMatrix after DrawRange for mode " + mode); - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matrixBuf); + final FloatBuffer matrixBuf = modelviewAfterCall(testList); assertEquals(0.5f, matrixBuf.get(0), 0.0001f, "Scale X for mode " + mode); assertEquals(0.5f, matrixBuf.get(5), 0.0001f, "Scale Y for mode " + mode); - - GLStateManager.glPopMatrix(); } } @@ -846,38 +569,16 @@ class GLSM_DisplayList_TransformCollapsing_Test { GLStateManager.glScalef(0.5f, 0.5f, 0.5f); - final Tessellator tess = Tessellator.instance; - final float[][] corners = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f } }; - tess.rawBuffer = new int[4 * 8]; - for (int i = 0; i < 4; i++) { - final int base = i * 8; - tess.rawBuffer[base] = Float.floatToRawIntBits(corners[i][0]); - tess.rawBuffer[base + 1] = Float.floatToRawIntBits(corners[i][1]); - tess.rawBuffer[base + 3] = Float.floatToRawIntBits(i == 1 || i == 2 ? 1.0f : 0.0f); - tess.rawBuffer[base + 4] = Float.floatToRawIntBits(i >= 2 ? 1.0f : 0.0f); - } - tess.isDrawing = true; - tess.drawMode = GL11.GL_QUADS; - tess.hasTexture = true; - tess.rawBufferIndex = 4 * 8; - tess.vertexCount = 4; - TessellatorManager.interceptDraw(tess); + rawQuad(1.0f); GLStateManager.glEndList(); CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList); assertNotNull(compiled); - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matrixBuf); + final FloatBuffer matrixBuf = modelviewAfterCall(testList); assertEquals(0.5f, matrixBuf.get(0), 0.0001f, "Scale X after playback"); assertEquals(0.5f, matrixBuf.get(5), 0.0001f, "Scale Y after playback"); - - GLStateManager.glPopMatrix(); } // ==================== Optimizer Push/Pop Reset Tests ==================== @@ -898,12 +599,7 @@ class GLSM_DisplayList_TransformCollapsing_Test { GLStateManager.glTranslatef(10.0f, 0.0f, 0.0f); GLStateManager.glRotatef(45.0f, 0.0f, 1.0f, 0.0f); - GLStateManager.glBegin(GL11.GL_QUADS); - GLStateManager.glVertex3f(0.0f, 0.0f, 0.0f); - GLStateManager.glVertex3f(1.0f, 0.0f, 0.0f); - GLStateManager.glVertex3f(1.0f, 1.0f, 0.0f); - GLStateManager.glVertex3f(0.0f, 1.0f, 0.0f); - GLStateManager.glEnd(); + immediateQuad(1.0f); GLStateManager.glPopMatrix(); GLStateManager.glEndList(); @@ -930,18 +626,11 @@ class GLSM_DisplayList_TransformCollapsing_Test { assertTrue(drawIndex < popIndex, "DRAW before POP"); // Verify final matrix after pop = T1 - GLStateManager.glPushMatrix(); - GLStateManager.glLoadIdentity(); - GLStateManager.glCallList(testList); - - FloatBuffer matrixBuf = BufferUtils.createFloatBuffer(16); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matrixBuf); + final FloatBuffer matrixBuf = modelviewAfterCall(testList); assertEquals(2.0f, matrixBuf.get(0), 0.0001f, "Scale X after pop"); assertEquals(2.0f, matrixBuf.get(5), 0.0001f, "Scale Y after pop"); assertEquals(5.0f, matrixBuf.get(12), 0.0001f, "Translate X after pop"); - - GLStateManager.glPopMatrix(); } @Test diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_UnitTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_UnitTest.java index d844e2718..ab3a51cc0 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_UnitTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_DisplayList_UnitTest.java @@ -1,7 +1,16 @@ package com.gtnewhorizons.angelica.glsm; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL14; + +import java.nio.FloatBuffer; +import java.util.function.DoubleSupplier; +import java.util.stream.Stream; import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -9,128 +18,77 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @GLCompatTest class GLSM_DisplayList_UnitTest { - @Test - void testDisplayListCompileCall() { - verifyState(GL11.GL_LIGHTING, false, "GL_LIGHTING Initial State"); - - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE); - GLStateManager.glEnable(GL11.GL_LIGHTING); - GLStateManager.glEndList(); - - verifyState(GL11.GL_LIGHTING, false, "GL_LIGHTING Post Display List Compile"); - - GLStateManager.glCallList(list); - - verifyState(GL11.GL_LIGHTING, true, "GL_LIGHTING Post Display List Call"); - - // Reset state that we changed during test - GLStateManager.glDisable(GL11.GL_LIGHTING); + /** One recordable state command: {@code apply} moves the driver value read by {@code driver} to {@code applied}. */ + private static Arguments state(String name, Runnable apply, Runnable reset, DoubleSupplier driver, double applied) { + return Arguments.of(name, apply, reset, driver, applied); } - @Test - void testDisplayListCompileExecute() { - verifyState(GL11.GL_LIGHTING, false, "GL_LIGHTING Initial State"); - - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE); - GLStateManager.glEnable(GL11.GL_LIGHTING); - GLStateManager.glEndList(); - - verifyState(GL11.GL_LIGHTING, true, "GL_LIGHTING Post Display List Compile and Execute"); - - // Reset state that we changed during test - GLStateManager.glDisable(GL11.GL_LIGHTING); + private static double lighting() { + final boolean enabled = GLStateManager.glIsEnabled(GL11.GL_LIGHTING); + verifyState(GL11.GL_LIGHTING, enabled, "driver must agree with the GLSM cache"); + return enabled ? 1.0 : 0.0; } - // ==================== CullFace Tests ==================== - - @Test - void testCullFaceInDisplayList() { - // Get initial cull face mode - int initialMode = GL11.glGetInteger(GL11.GL_CULL_FACE_MODE); - - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE); - GLStateManager.glCullFace(GL11.GL_FRONT); - GLStateManager.glEndList(); - - // After compile, mode should be unchanged - assertEquals(initialMode, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "CullFace mode should be unchanged after GL_COMPILE"); - - // Call the display list - GLStateManager.glCallList(list); - - // Now mode should be GL_FRONT - assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "CullFace mode should be GL_FRONT after calling display list"); - - // Cleanup - GLStateManager.glCullFace(GL11.GL_BACK); // Reset to default - GLStateManager.glDeleteLists(list, 1); + private static double blendColorRed() { + final FloatBuffer color = BufferUtils.createFloatBuffer(16); + GL11.glGetFloat(GL14.GL_BLEND_COLOR, color); + return color.get(0); } - @Test - void testCullFaceCompileAndExecute() { - // Get initial cull face mode - int initialMode = GL11.glGetInteger(GL11.GL_CULL_FACE_MODE); - - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE); - GLStateManager.glCullFace(GL11.GL_FRONT_AND_BACK); - GLStateManager.glEndList(); - - // After compile and execute, mode should be changed - assertEquals(GL11.GL_FRONT_AND_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "CullFace mode should be GL_FRONT_AND_BACK after GL_COMPILE_AND_EXECUTE"); - - // Cleanup - GLStateManager.glCullFace(GL11.GL_BACK); - GLStateManager.glDeleteLists(list, 1); + static Stream states() { + return Stream.of( + state("glEnable(GL_LIGHTING)", () -> GLStateManager.glEnable(GL11.GL_LIGHTING), () -> GLStateManager.glDisable(GL11.GL_LIGHTING), + GLSM_DisplayList_UnitTest::lighting, 1.0), + state("glCullFace", () -> GLStateManager.glCullFace(GL11.GL_FRONT), () -> GLStateManager.glCullFace(GL11.GL_BACK), + () -> GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), GL11.GL_FRONT), + state("glLogicOp", () -> GLStateManager.glLogicOp(GL11.GL_XOR), () -> GLStateManager.glLogicOp(GL11.GL_COPY), + () -> GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), GL11.GL_XOR), + state("glClearDepth", () -> GLStateManager.glClearDepth(0.5), () -> GLStateManager.glClearDepth(1.0), + () -> GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE), 0.5), + state("glBlendColor", () -> GLStateManager.glBlendColor(0.5f, 0.4f, 0.6f, 0.8f), () -> GLStateManager.glBlendColor(0f, 0f, 0f, 0f), + GLSM_DisplayList_UnitTest::blendColorRed, 0.5), + state("glFrontFace", () -> GLStateManager.glFrontFace(GL11.GL_CW), () -> GLStateManager.glFrontFace(GL11.GL_CCW), + () -> GL11.glGetInteger(GL11.GL_FRONT_FACE), GL11.GL_CW), + state("glHint", () -> GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST), () -> GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_DONT_CARE), + () -> GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT), GL11.GL_NICEST)); } - // ==================== LogicOp Tests ==================== + @ParameterizedTest(name = "{0}") + @MethodSource("states") + void compiledStateChangeAppliesOnlyWhenTheListIsCalled(String name, Runnable apply, Runnable reset, DoubleSupplier driver, double applied) { + final double initial = driver.getAsDouble(); + final int list = GL11.glGenLists(1); + try { + GLStateManager.glNewList(list, GL11.GL_COMPILE); + apply.run(); + GLStateManager.glEndList(); + assertEquals(initial, driver.getAsDouble(), 0.0001, name + " must not execute under GL_COMPILE"); - @Test - void testLogicOpInDisplayList() { - // Get initial logic op - int initialOp = GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE); - - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE); - GLStateManager.glLogicOp(GL11.GL_XOR); - GLStateManager.glEndList(); - - // After compile, op should be unchanged - assertEquals(initialOp, GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), "LogicOp should be unchanged after GL_COMPILE"); - - // Call the display list - GLStateManager.glCallList(list); - - // Now op should be GL_XOR - assertEquals(GL11.GL_XOR, GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), "LogicOp should be GL_XOR after calling display list"); - - // Cleanup - GLStateManager.glLogicOp(GL11.GL_COPY); // Reset to default - GLStateManager.glDeleteLists(list, 1); + GLStateManager.glCallList(list); + assertEquals(applied, driver.getAsDouble(), 0.0001, name + " must apply when the list is called"); + } finally { + reset.run(); + GLStateManager.glDeleteLists(list, 1); + } } - @Test - void testLogicOpCompileAndExecute() { - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE); - GLStateManager.glLogicOp(GL11.GL_INVERT); - GLStateManager.glEndList(); - - // After compile and execute, op should be changed - assertEquals(GL11.GL_INVERT, GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), "LogicOp should be GL_INVERT after GL_COMPILE_AND_EXECUTE"); - - // Cleanup - GLStateManager.glLogicOp(GL11.GL_COPY); - GLStateManager.glDeleteLists(list, 1); + @ParameterizedTest(name = "{0}") + @MethodSource("states") + void compileAndExecuteAppliesStateChangeImmediately(String name, Runnable apply, Runnable reset, DoubleSupplier driver, double applied) { + final int list = GL11.glGenLists(1); + try { + GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE); + apply.run(); + GLStateManager.glEndList(); + assertEquals(applied, driver.getAsDouble(), 0.0001, name + " must apply under GL_COMPILE_AND_EXECUTE"); + } finally { + reset.run(); + GLStateManager.glDeleteLists(list, 1); + } } @Test void testMultipleStateChangesInDisplayList() { - // Test that multiple state commands work together int list = GL11.glGenLists(1); GLStateManager.glNewList(list, GL11.GL_COMPILE); GLStateManager.glCullFace(GL11.GL_FRONT); @@ -138,173 +96,15 @@ class GLSM_DisplayList_UnitTest { GLStateManager.glEnable(GL11.GL_LIGHTING); GLStateManager.glEndList(); - // Call the list GLStateManager.glCallList(list); - // Verify all states changed assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "CullFace should be GL_FRONT"); assertEquals(GL11.GL_AND, GL11.glGetInteger(GL11.GL_LOGIC_OP_MODE), "LogicOp should be GL_AND"); verifyState(GL11.GL_LIGHTING, true, "GL_LIGHTING should be enabled"); - // Cleanup GLStateManager.glCullFace(GL11.GL_BACK); GLStateManager.glLogicOp(GL11.GL_COPY); GLStateManager.glDisable(GL11.GL_LIGHTING); GLStateManager.glDeleteLists(list, 1); } - - // ==================== ClearDepth Tests ==================== - - @Test - void testClearDepthInDisplayList() { - // Get initial clear depth - double initialDepth = GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE); - - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE); - GLStateManager.glClearDepth(0.5); - GLStateManager.glEndList(); - - // After compile, depth should be unchanged - assertEquals(initialDepth, GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE), 0.0001, "ClearDepth should be unchanged after GL_COMPILE"); - - // Call the display list - GLStateManager.glCallList(list); - - // Now depth should be 0.5 - assertEquals(0.5, GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE), 0.0001, "ClearDepth should be 0.5 after calling display list"); - - // Cleanup - GLStateManager.glClearDepth(1.0); // Reset to default - GLStateManager.glDeleteLists(list, 1); - } - - @Test - void testClearDepthCompileAndExecute() { - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE); - GLStateManager.glClearDepth(0.25); - GLStateManager.glEndList(); - - // After compile and execute, depth should be changed - assertEquals(0.25, GL11.glGetDouble(GL11.GL_DEPTH_CLEAR_VALUE), 0.0001, "ClearDepth should be 0.25 after GL_COMPILE_AND_EXECUTE"); - - // Cleanup - GLStateManager.glClearDepth(1.0); - GLStateManager.glDeleteLists(list, 1); - } - - // ==================== BlendColor Tests ==================== - - @Test - void testBlendColorInDisplayList() { - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE); - GLStateManager.glBlendColor(0.2f, 0.4f, 0.6f, 0.8f); - GLStateManager.glEndList(); - - // Call the display list - should not throw - GLStateManager.glCallList(list); - - // Cleanup - GLStateManager.glBlendColor(0f, 0f, 0f, 0f); - GLStateManager.glDeleteLists(list, 1); - } - - @Test - void testBlendColorCompileAndExecute() { - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE); - GLStateManager.glBlendColor(0.5f, 0.5f, 0.5f, 1.0f); - GLStateManager.glEndList(); - - // Should not throw - display list was executed - GLStateManager.glCallList(list); - - // Cleanup - GLStateManager.glBlendColor(0f, 0f, 0f, 0f); - GLStateManager.glDeleteLists(list, 1); - } - - // ==================== FrontFace Tests ==================== - - @Test - void testFrontFaceInDisplayList() { - // Get initial front face - int initialMode = GL11.glGetInteger(GL11.GL_FRONT_FACE); - - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE); - GLStateManager.glFrontFace(GL11.GL_CW); - GLStateManager.glEndList(); - - // After compile, mode should be unchanged - assertEquals(initialMode, GL11.glGetInteger(GL11.GL_FRONT_FACE), "FrontFace should be unchanged after GL_COMPILE"); - - // Call the display list - GLStateManager.glCallList(list); - - // Now mode should be GL_CW - assertEquals(GL11.GL_CW, GL11.glGetInteger(GL11.GL_FRONT_FACE), "FrontFace should be GL_CW after calling display list"); - - // Cleanup - GLStateManager.glFrontFace(GL11.GL_CCW); // Reset to default - GLStateManager.glDeleteLists(list, 1); - } - - @Test - void testFrontFaceCompileAndExecute() { - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE); - GLStateManager.glFrontFace(GL11.GL_CW); - GLStateManager.glEndList(); - - // After compile and execute, mode should be changed - assertEquals(GL11.GL_CW, GL11.glGetInteger(GL11.GL_FRONT_FACE), "FrontFace should be GL_CW after GL_COMPILE_AND_EXECUTE"); - - // Cleanup - GLStateManager.glFrontFace(GL11.GL_CCW); - GLStateManager.glDeleteLists(list, 1); - } - - // ==================== Hint Tests ==================== - - @Test - void testHintInDisplayList() { - // Get initial hint value - int initialHint = GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT); - - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE); - GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST); - GLStateManager.glEndList(); - - // After compile, hint should be unchanged - assertEquals(initialHint, GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT), "Hint should be unchanged after GL_COMPILE"); - - // Call the display list - GLStateManager.glCallList(list); - - // Now hint should be GL_NICEST - assertEquals(GL11.GL_NICEST, GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT), "Hint should be GL_NICEST after calling display list"); - - // Cleanup - GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_DONT_CARE); // Reset to default - GLStateManager.glDeleteLists(list, 1); - } - - @Test - void testHintCompileAndExecute() { - int list = GL11.glGenLists(1); - GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE); - GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_FASTEST); - GLStateManager.glEndList(); - - // After compile and execute, hint should be changed - assertEquals(GL11.GL_FASTEST, GL11.glGetInteger(GL11.GL_LINE_SMOOTH_HINT), "Hint should be GL_FASTEST after GL_COMPILE_AND_EXECUTE"); - - // Cleanup - GLStateManager.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_DONT_CARE); - GLStateManager.glDeleteLists(list, 1); - } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_GLU_UnitTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_GLU_UnitTest.java index 221b64534..aadc773f9 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_GLU_UnitTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_GLU_UnitTest.java @@ -3,12 +3,16 @@ package com.gtnewhorizons.angelica.glsm; import org.joml.Matrix4f; import org.joml.Vector4f; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -20,37 +24,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @GLCompatTest public class GLSM_GLU_UnitTest { - private static final FloatBuffer buffer = BufferUtils.createFloatBuffer(16); - private Matrix4f getModelviewMatrix() { - buffer.clear(); - GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, buffer); - return new Matrix4f(buffer); + return new Matrix4f(GlTestSupport.modelview()); } private Matrix4f getProjectionMatrix() { - buffer.clear(); - GLStateManager.glGetFloat(GL11.GL_PROJECTION_MATRIX, buffer); - return new Matrix4f(buffer); + return new Matrix4f(GlTestSupport.matrix(GL11.GL_PROJECTION_MATRIX)); } private void assertMatrixEquals(Matrix4f expected, Matrix4f actual, String message) { assertTrue(expected.equals(actual, 0.001f), () -> message + "\nExpected:\n" + expected + "\nActual:\n" + actual); } - @Test - void gluPerspective_matchesJOML() { - GLStateManager.glMatrixMode(GL11.GL_PROJECTION); - GLStateManager.glLoadIdentity(); - - GLStateManager.gluPerspective(60.0f, 1.5f, 0.1f, 1000.0f); - - Matrix4f expected = new Matrix4f().perspective((float) Math.toRadians(60.0f), 1.5f, 0.1f, 1000.0f); - assertMatrixEquals(expected, getProjectionMatrix(), "gluPerspective standard"); - - GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); - } - @Test void gluPerspective_accumulates() { GLStateManager.glMatrixMode(GL11.GL_PROJECTION); @@ -67,40 +52,6 @@ public class GLSM_GLU_UnitTest { GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); } - @Test - void gluLookAt_matchesJOML() { - GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); - GLStateManager.glLoadIdentity(); - - GLStateManager.gluLookAt( - 0.0f, 0.0f, 5.0f, // eye - 0.0f, 0.0f, 0.0f, // center - 0.0f, 1.0f, 0.0f); // up - - Matrix4f expected = new Matrix4f().lookAt( - 0.0f, 0.0f, 5.0f, - 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f); - assertMatrixEquals(expected, getModelviewMatrix(), "gluLookAt simple"); - } - - @Test - void gluLookAt_offAxis() { - GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); - GLStateManager.glLoadIdentity(); - - GLStateManager.gluLookAt( - 3.0f, 4.0f, 5.0f, - 1.0f, 2.0f, 0.0f, - 0.0f, 1.0f, 0.0f); - - Matrix4f expected = new Matrix4f().lookAt( - 3.0f, 4.0f, 5.0f, - 1.0f, 2.0f, 0.0f, - 0.0f, 1.0f, 0.0f); - assertMatrixEquals(expected, getModelviewMatrix(), "gluLookAt off-axis"); - } - @Test void gluLookAt_accumulates() { GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); @@ -208,25 +159,23 @@ public class GLSM_GLU_UnitTest { GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); } - @Test - void gluErrorString_glErrors() { - assertEquals("No error", GLStateManager.gluErrorString(GL11.GL_NO_ERROR)); - assertEquals("Invalid enum", GLStateManager.gluErrorString(GL11.GL_INVALID_ENUM)); - assertEquals("Invalid value", GLStateManager.gluErrorString(GL11.GL_INVALID_VALUE)); - assertEquals("Invalid operation", GLStateManager.gluErrorString(GL11.GL_INVALID_OPERATION)); - assertEquals("Out of memory", GLStateManager.gluErrorString(GL11.GL_OUT_OF_MEMORY)); + static Stream errorStrings() { + return Stream.of( + Arguments.of(GL11.GL_NO_ERROR, "No error"), + Arguments.of(GL11.GL_INVALID_ENUM, "Invalid enum"), + Arguments.of(GL11.GL_INVALID_VALUE, "Invalid value"), + Arguments.of(GL11.GL_INVALID_OPERATION, "Invalid operation"), + Arguments.of(GL11.GL_OUT_OF_MEMORY, "Out of memory"), + Arguments.of(100900, "Invalid enum (glu)"), + Arguments.of(100901, "Invalid value (glu)"), + Arguments.of(100902, "Out of memory (glu)"), + Arguments.of(99999, "Unknown error: 99999")); } - @Test - void gluErrorString_gluErrors() { - assertEquals("Invalid enum (glu)", GLStateManager.gluErrorString(100900)); - assertEquals("Invalid value (glu)", GLStateManager.gluErrorString(100901)); - assertEquals("Out of memory (glu)", GLStateManager.gluErrorString(100902)); - } - - @Test - void gluErrorString_unknownCode() { - assertEquals("Unknown error: 99999", GLStateManager.gluErrorString(99999)); + @ParameterizedTest(name = "{1}") + @MethodSource("errorStrings") + void gluErrorString(int code, String expected) { + assertEquals(expected, GLStateManager.gluErrorString(code)); } @Test diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ImageUnitBindingCache_UnitTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ImageUnitBindingCache_UnitTest.java index f526fe82a..6fa976e66 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ImageUnitBindingCache_UnitTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ImageUnitBindingCache_UnitTest.java @@ -2,11 +2,13 @@ package com.gtnewhorizons.angelica.glsm; import com.gtnewhorizons.angelica.glsm.states.ImageUnitBinding; import org.junit.jupiter.api.Test; +import org.lwjgl.opengl.ContextCapabilities; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL42; +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -16,17 +18,12 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue; @GLCoreTest public class GLSM_ImageUnitBindingCache_UnitTest { - private static void drainErrors() { - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} - } - private static void unbind(int unit) { - GLStateManager.glBindImageTexture(unit, 0, 0, false, 0, GL15.GL_READ_ONLY, 0); + GLStateManager.glBindImageTexture(unit, 0, 0, false, 0, GL15.GL_READ_ONLY, GL30.GL_R8); } @Test void bindThenQueryReturnsFullTuple() { - drainErrors(); final int tex = GL11.glGenTextures(); try { GLStateManager.glBindImageTexture(2, tex, 1, false, 3, GL15.GL_READ_ONLY, GL30.GL_RGBA32UI); @@ -40,13 +37,11 @@ public class GLSM_ImageUnitBindingCache_UnitTest { } finally { unbind(2); GLStateManager.glDeleteTextures(tex); - drainErrors(); } } @Test void saveRestoreRoundTripsTheWholeTuple() { - drainErrors(); final int a = GL11.glGenTextures(); final int b = GL11.glGenTextures(); try { @@ -55,7 +50,7 @@ public class GLSM_ImageUnitBindingCache_UnitTest { assertNotNull(live); final ImageUnitBinding saved = live.copy(); - GLStateManager.glBindImageTexture(0, b, 0, true, 0, GL15.GL_WRITE_ONLY, 0); + GLStateManager.glBindImageTexture(0, b, 0, true, 0, GL15.GL_WRITE_ONLY, GL30.GL_R8); assertEquals(b, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 0)); GLStateManager.glBindImageTexture(0, saved.getTexture(), saved.getLevel(), saved.isLayered(), saved.getLayer(), saved.getAccess(), saved.getFormat()); @@ -68,30 +63,26 @@ public class GLSM_ImageUnitBindingCache_UnitTest { unbind(0); GLStateManager.glDeleteTextures(a); GLStateManager.glDeleteTextures(b); - drainErrors(); } } @Test void resetToZeroIsNotARestore() { - drainErrors(); final int tex = GL11.glGenTextures(); try { GLStateManager.glBindImageTexture(0, tex, 1, true, 0, GL15.GL_READ_WRITE, GL30.GL_R32UI); - GLStateManager.glBindImageTexture(0, 0, 0, true, 0, GL15.GL_WRITE_ONLY, 0); + GLStateManager.glBindImageTexture(0, 0, 0, true, 0, GL15.GL_WRITE_ONLY, GL30.GL_R8); assertEquals(0, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 0)); assertEquals(0, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_LEVEL, 0)); } finally { unbind(0); GLStateManager.glDeleteTextures(tex); - drainErrors(); } } @Test void deletingTheBoundTextureClearsTheImageUnit() { - drainErrors(); final int tex = GL11.glGenTextures(); try { GLStateManager.glBindImageTexture(1, tex, 0, true, 0, GL15.GL_READ_WRITE, GL30.GL_R32UI); @@ -101,13 +92,11 @@ public class GLSM_ImageUnitBindingCache_UnitTest { assertEquals(0, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 1), "GL unbinds a deleted texture from every image unit"); } finally { unbind(1); - drainErrors(); } } @Test void unitsAreIndependentAndUnitZeroIsNotSpecial() { - drainErrors(); final int a = GL11.glGenTextures(); final int b = GL11.glGenTextures(); try { @@ -123,13 +112,11 @@ public class GLSM_ImageUnitBindingCache_UnitTest { unbind(3); GLStateManager.glDeleteTextures(a); GLStateManager.glDeleteTextures(b); - drainErrors(); } } @Test void outOfRangeUnitDoesNotCorruptNeighbours() { - drainErrors(); final int tex = GL11.glGenTextures(); try { GLStateManager.glBindImageTexture(0, tex, 0, true, 0, GL15.GL_READ_ONLY, GL30.GL_R32UI); @@ -137,11 +124,14 @@ public class GLSM_ImageUnitBindingCache_UnitTest { assertFalse(beyond == 0, "unit 0 must resolve for this test to mean anything"); GLStateManager.glBindImageTexture(beyond, tex, 0, true, 0, GL15.GL_READ_ONLY, GL30.GL_R32UI); + // Without image load/store the backend drops the call, so the driver never sees the unit. + final ContextCapabilities caps = GLStateManager.capabilities; + final boolean reachesDriver = caps.OpenGL42 || caps.GL_ARB_shader_image_load_store || caps.GL_EXT_shader_image_load_store; + assertEquals(reachesDriver ? GL11.GL_INVALID_VALUE : GL11.GL_NO_ERROR, GL11.glGetError(), "driver must reject the out-of-range unit"); assertEquals(tex, GLStateManager.glGetInteger(GL42.GL_IMAGE_BINDING_NAME, 0)); } finally { unbind(0); GLStateManager.glDeleteTextures(tex); - drainErrors(); } } @@ -151,9 +141,9 @@ public class GLSM_ImageUnitBindingCache_UnitTest { drainErrors(); final int tex = GL11.glGenTextures(); try { - GL11.glBindTexture(GL11.GL_TEXTURE_2D, tex); - GL42.glTexStorage2D(GL11.GL_TEXTURE_2D, 1, GL30.GL_R32UI, 4, 4); - GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0); + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex); + GLStateManager.glTexStorage2D(GL11.GL_TEXTURE_2D, 1, GL30.GL_R32UI, 4, 4); + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0); GLStateManager.glBindImageTexture(1, tex, 0, false, 0, GL15.GL_READ_ONLY, GL30.GL_R32UI); drainErrors(); @@ -164,7 +154,6 @@ public class GLSM_ImageUnitBindingCache_UnitTest { } finally { unbind(1); GLStateManager.glDeleteTextures(tex); - drainErrors(); } } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_LegacyStackQueries_GLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_LegacyStackQueries_GLTest.java index 96236c5bb..6673aac71 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_LegacyStackQueries_GLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_LegacyStackQueries_GLTest.java @@ -1,22 +1,34 @@ package com.gtnewhorizons.angelica.glsm; -import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags; -import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage; -import com.gtnewhorizons.angelica.glsm.ffp.VAOManager; import com.gtnewhorizons.angelica.glsm.ffp.VertexKey; import com.gtnewhorizons.angelica.glsm.states.PixelStoreState; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL14; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL21; import org.lwjgl.opengl.GL30; +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; +import java.util.stream.Stream; +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.HINT_TARGETS; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.assertEnableRoundTrip; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.drainClientAttribStack; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.hex; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.legacyPnames; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.popAttribTo; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.setEnabled; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -27,44 +39,12 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue; @GLCoreTest public class GLSM_LegacyStackQueries_GLTest { - private static final int[] SINGLE_VALUE_QUERIES = { - GL11.GL_ATTRIB_STACK_DEPTH, - GL11.GL_CLIENT_ATTRIB_STACK_DEPTH, - GL11.GL_TEXTURE_STACK_DEPTH, - GL11.GL_MAX_ATTRIB_STACK_DEPTH, - GL11.GL_MAX_CLIENT_ATTRIB_STACK_DEPTH, - GL11.GL_MAX_MODELVIEW_STACK_DEPTH, - GL11.GL_MAX_PROJECTION_STACK_DEPTH, - GL11.GL_MAX_TEXTURE_STACK_DEPTH, - GL13.GL_MAX_TEXTURE_UNITS, - GL11.GL_MAX_LIGHTS, - GL11.GL_STENCIL_BITS, - GL11.GL_DEPTH_BITS, - }; + private static final int[] LEGACY_PNAMES = legacyPnames(); - private static boolean positionArrayEnabled() { - final VAOManager.Attrib position = GLStateManager.ctx().vaos.attrib(Usage.POSITION.getAttributeLocation()); - return position != null && position.enabled; - } - - private static boolean colorArrayEnabled() { - return (GLStateManager.ctx().vaos.getVertexFlags() & VertexFlags.COLOR_BIT) != 0; - } - - private static void drainErrors() { - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} - } - - private static void drainClientAttribStack() { - while (GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH) > 0) { - GLStateManager.glPopClientAttrib(); - } - } - - private static void drainAttribStack() { - while (GLStateManager.getAttribDepth() > 0) { - GLStateManager.glPopAttrib(); - } + private static void assumeDriverRejectsLegacyPnames() { + drainErrors(); + GL11.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH); + assumeTrue(GL11.glGetError() != GL11.GL_NO_ERROR, "driver accepts GL_ATTRIB_STACK_DEPTH, so a clean error state proves nothing"); } private static int pushToLimit(int depthPname, int maxPname) { @@ -95,9 +75,7 @@ public class GLSM_LegacyStackQueries_GLTest { assertEquals(base, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH)); assertEquals(GLStateManager.MAX_ATTRIB_STACK_DEPTH, GLStateManager.glGetInteger(GL11.GL_MAX_ATTRIB_STACK_DEPTH)); } finally { - while (GLStateManager.getAttribDepth() > base) { - GLStateManager.glPopAttrib(); - } + popAttribTo(base); } } @@ -150,42 +128,24 @@ public class GLSM_LegacyStackQueries_GLTest { } } - @Test - void maxModelviewStackDepthIsExactlyReachable() { - try { - GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); - assertEquals(1, GLStateManager.glGetInteger(GL11.GL_MODELVIEW_STACK_DEPTH)); - assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_MODELVIEW_STACK_DEPTH), pushToLimit(GL11.GL_MODELVIEW_STACK_DEPTH, GL11.GL_MAX_MODELVIEW_STACK_DEPTH)); - } finally { - GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); - popToBase(GL11.GL_MODELVIEW_STACK_DEPTH); - } + static Stream matrixStacks() { + return Stream.of( + Arguments.of("modelview", GL11.GL_MODELVIEW, GL11.GL_MODELVIEW_STACK_DEPTH, GL11.GL_MAX_MODELVIEW_STACK_DEPTH), + Arguments.of("projection", GL11.GL_PROJECTION, GL11.GL_PROJECTION_STACK_DEPTH, GL11.GL_MAX_PROJECTION_STACK_DEPTH), + Arguments.of("texture", GL11.GL_TEXTURE, GL11.GL_TEXTURE_STACK_DEPTH, GL11.GL_MAX_TEXTURE_STACK_DEPTH)); } - @Test - void maxProjectionStackDepthIsExactlyReachable() { - try { - GLStateManager.glMatrixMode(GL11.GL_PROJECTION); - assertEquals(1, GLStateManager.glGetInteger(GL11.GL_PROJECTION_STACK_DEPTH)); - assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_PROJECTION_STACK_DEPTH), pushToLimit(GL11.GL_PROJECTION_STACK_DEPTH, GL11.GL_MAX_PROJECTION_STACK_DEPTH)); - } finally { - GLStateManager.glMatrixMode(GL11.GL_PROJECTION); - popToBase(GL11.GL_PROJECTION_STACK_DEPTH); - GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); - } - } - - @Test - void maxTextureStackDepthIsExactlyReachable() { + @ParameterizedTest(name = "{0}") + @MethodSource("matrixStacks") + void maxMatrixStackDepthIsExactlyReachable(String name, int mode, int depthPname, int maxPname) { try { GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - GLStateManager.glMatrixMode(GL11.GL_TEXTURE); - assertEquals(1, GLStateManager.glGetInteger(GL11.GL_TEXTURE_STACK_DEPTH)); - assertEquals(GLStateManager.glGetInteger(GL11.GL_MAX_TEXTURE_STACK_DEPTH), pushToLimit(GL11.GL_TEXTURE_STACK_DEPTH, GL11.GL_MAX_TEXTURE_STACK_DEPTH)); + GLStateManager.glMatrixMode(mode); + assertEquals(1, GLStateManager.glGetInteger(depthPname)); + assertEquals(GLStateManager.glGetInteger(maxPname), pushToLimit(depthPname, maxPname)); } finally { - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - GLStateManager.glMatrixMode(GL11.GL_TEXTURE); - popToBase(GL11.GL_TEXTURE_STACK_DEPTH); + GLStateManager.glMatrixMode(mode); + popToBase(depthPname); GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); } } @@ -203,14 +163,12 @@ public class GLSM_LegacyStackQueries_GLTest { final int savedRead = glCtx.readFramebuffer; try { GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0); - drainErrors(); assertEquals( GL30.glGetFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_STENCIL, GL30.GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE), GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS)); assertEquals( GL30.glGetFramebufferAttachmentParameteri(GL30.GL_DRAW_FRAMEBUFFER, GL11.GL_DEPTH, GL30.GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE), GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS)); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } finally { GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, savedDraw); GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, savedRead); @@ -226,7 +184,6 @@ public class GLSM_LegacyStackQueries_GLTest { final int depthOnly = GLStateManager.glGenRenderbuffers(); final int depthStencil = GLStateManager.glGenRenderbuffers(); try { - drainErrors(); GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthOnly); GLStateManager.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL14.GL_DEPTH_COMPONENT24, 16, 16); GLStateManager.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthStencil); @@ -247,7 +204,6 @@ public class GLSM_LegacyStackQueries_GLTest { GLStateManager.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, depthStencil); assertEquals(8, GLStateManager.glGetInteger(GL11.GL_STENCIL_BITS)); assertEquals(24, GLStateManager.glGetInteger(GL11.GL_DEPTH_BITS)); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } finally { GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, savedDraw); GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, savedRead); @@ -257,33 +213,6 @@ public class GLSM_LegacyStackQueries_GLTest { } } - @Test - void intBufferOverloadMatchesAndDriverIsNotAsked() { - final int base = GLStateManager.getAttribDepth(); - final IntBuffer buf = BufferUtils.createIntBuffer(16); - try { - GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT); - GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); - GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); - drainErrors(); - GL11.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH); - assumeTrue(GL11.glGetError() != GL11.GL_NO_ERROR, "driver accepts GL_ATTRIB_STACK_DEPTH, so a clean error state proves nothing"); - for (final int pname : SINGLE_VALUE_QUERIES) { - final int direct = GLStateManager.glGetInteger(pname); - buf.put(0, -12345); - GLStateManager.glGetInteger(pname, buf); - assertEquals(direct, buf.get(0), "IntBuffer overload for 0x" + Integer.toHexString(pname)); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "driver error after querying 0x" + Integer.toHexString(pname)); - } - } finally { - GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); - drainClientAttribStack(); - while (GLStateManager.getAttribDepth() > base) { - GLStateManager.glPopAttrib(); - } - } - } - @Test void attribDepthUnchangedWhileCompilingDisplayList() { final int base = GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH); @@ -306,14 +235,12 @@ public class GLSM_LegacyStackQueries_GLTest { @Test void popAttribOnEmptyStackIsIgnored() { - drainAttribStack(); - drainErrors(); + popAttribTo(0); assertEquals(0, GLStateManager.getAttribDepth()); assertDoesNotThrow(GLStateManager::glPopAttrib); assertEquals(0, GLStateManager.glGetInteger(GL11.GL_ATTRIB_STACK_DEPTH)); assertDoesNotThrow(GLStateManager::glPopAttrib); assertEquals(0, GLStateManager.getAttribDepth()); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } @Test @@ -322,8 +249,11 @@ public class GLSM_LegacyStackQueries_GLTest { final int unpackBuffer = GLStateManager.glGenBuffers(); final int packBuffer = GLStateManager.glGenBuffers(); try { - GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4); - GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4); + // Pack and unpack start from different values, so a pop that crosses them is caught + GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2); + GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 8); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 3); + GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 5); GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0); GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0); GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0); @@ -332,31 +262,41 @@ public class GLSM_LegacyStackQueries_GLTest { GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); - GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 2); + GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 6); + GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 7); GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 7); GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, unpackBuffer); GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, packBuffer); assertEquals(1, glCtx.pixelUnpackState.alignment()); - assertEquals(2, glCtx.pixelPackState.alignment()); + assertEquals(4, glCtx.pixelPackState.alignment()); + assertEquals(6, glCtx.pixelUnpackState.skipRows()); + assertEquals(7, glCtx.pixelPackState.skipRows()); assertEquals(7, glCtx.pixelPackState.rowLength()); - assertEquals(2, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT)); + assertEquals(4, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT)); + assertEquals(6, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS)); + assertEquals(7, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS)); GLStateManager.glPopClientAttrib(); assertSame(unpackBefore, glCtx.pixelUnpackState); assertSame(packBefore, glCtx.pixelPackState); - assertEquals(4, GLStateManager.glGetInteger(GL11.GL_UNPACK_ALIGNMENT)); + assertEquals(2, GLStateManager.glGetInteger(GL11.GL_UNPACK_ALIGNMENT)); + assertEquals(8, GLStateManager.glGetInteger(GL11.GL_PACK_ALIGNMENT)); assertEquals(0, GLStateManager.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING)); assertEquals(0, GLStateManager.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING)); - assertEquals(4, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT)); - assertEquals(4, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT)); + assertEquals(2, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT)); + assertEquals(8, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT)); + assertEquals(3, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS)); + assertEquals(5, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS)); assertEquals(0, GL11.glGetInteger(GL11.GL_PACK_ROW_LENGTH)); assertEquals(0, GL11.glGetInteger(GL21.GL_PIXEL_UNPACK_BUFFER_BINDING)); assertEquals(0, GL11.glGetInteger(GL21.GL_PIXEL_PACK_BUFFER_BINDING)); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } finally { drainClientAttribStack(); GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4); GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4); + GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0); + GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 0); GLStateManager.glPixelStorei(GL11.GL_PACK_ROW_LENGTH, 0); GLStateManager.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0); GLStateManager.glBindBuffer(GL21.GL_PIXEL_PACK_BUFFER, 0); @@ -428,122 +368,6 @@ public class GLSM_LegacyStackQueries_GLTest { } } - @Test - void packAndUnpackRestoreToTheirOwnPriorValues() { - final GLContextState glCtx = GLStateManager.ctx(); - try { - GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 2); - GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 8); - GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 3); - GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 5); - - GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); - GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); - GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4); - GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 6); - GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 7); - assertEquals(1, glCtx.pixelUnpackState.alignment()); - assertEquals(4, glCtx.pixelPackState.alignment()); - assertEquals(6, glCtx.pixelUnpackState.skipRows()); - assertEquals(7, glCtx.pixelPackState.skipRows()); - assertEquals(6, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS)); - assertEquals(7, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS)); - GLStateManager.glPopClientAttrib(); - - assertEquals(2, glCtx.pixelUnpackState.alignment()); - assertEquals(8, glCtx.pixelPackState.alignment()); - assertEquals(3, glCtx.pixelUnpackState.skipRows()); - assertEquals(5, glCtx.pixelPackState.skipRows()); - assertEquals(2, GL11.glGetInteger(GL11.GL_UNPACK_ALIGNMENT)); - assertEquals(8, GL11.glGetInteger(GL11.GL_PACK_ALIGNMENT)); - assertEquals(3, GL11.glGetInteger(GL11.GL_UNPACK_SKIP_ROWS)); - assertEquals(5, GL11.glGetInteger(GL11.GL_PACK_SKIP_ROWS)); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); - } finally { - drainClientAttribStack(); - GLStateManager.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 4); - GLStateManager.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 4); - GLStateManager.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0); - GLStateManager.glPixelStorei(GL11.GL_PACK_SKIP_ROWS, 0); - } - } - - @Test - void vertexArrayEnableRestoredOnlyWithVertexArrayBit() { - final int savedVao = GLStateManager.getBoundVAO(); - final int vao = GLStateManager.glGenVertexArrays(); - final IntBuffer enabled = BufferUtils.createIntBuffer(16); - final int position = Usage.POSITION.getAttributeLocation(); - try { - GLStateManager.glBindVertexArray(vao); - drainErrors(); - assertFalse(positionArrayEnabled()); - - GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); - GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); - assertTrue(positionArrayEnabled()); - GLStateManager.glPopClientAttrib(); - assertFalse(positionArrayEnabled()); - GL20.glGetVertexAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED, enabled); - assertEquals(GL11.GL_FALSE, enabled.get(0)); - - GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); - GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); - GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); - GLStateManager.glPopClientAttrib(); - assertTrue(positionArrayEnabled()); - GL20.glGetVertexAttrib(position, GL20.GL_VERTEX_ATTRIB_ARRAY_ENABLED, enabled); - assertEquals(GL11.GL_TRUE, enabled.get(0)); - GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); - - GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); - GLStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); - GLStateManager.glPopClientAttrib(); - assertTrue(positionArrayEnabled()); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); - } finally { - drainClientAttribStack(); - GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); - GLStateManager.glBindVertexArray(savedVao); - GLStateManager.glDeleteVertexArrays(vao); - } - } - - @Test - void vertexArrayStateRestoredOnlyWithVertexArrayBit() { - final int savedVao = GLStateManager.getBoundVAO(); - final int vao = GLStateManager.glGenVertexArrays(); - try { - GLStateManager.glBindVertexArray(vao); - GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); - final int flagsBefore = GLStateManager.ctx().vaos.getVertexFlags(); - assertFalse(colorArrayEnabled()); - - GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); - GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY); - GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); - assertTrue(colorArrayEnabled()); - GLStateManager.glPopClientAttrib(); - assertFalse(colorArrayEnabled()); - assertEquals(flagsBefore, GLStateManager.ctx().vaos.getVertexFlags()); - assertEquals(0, GLStateManager.getClientActiveTextureUnit()); - - GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); - GLStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY); - GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); - GLStateManager.glPopClientAttrib(); - assertTrue(colorArrayEnabled()); - assertEquals(1, GLStateManager.getClientActiveTextureUnit()); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); - } finally { - drainClientAttribStack(); - GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); - GLStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY); - GLStateManager.glBindVertexArray(savedVao); - GLStateManager.glDeleteVertexArrays(vao); - } - } - @Test void intLightGettersConvertLikeMesa() { final FloatBuffer savedDiffuse = BufferUtils.createFloatBuffer(16); @@ -570,7 +394,6 @@ public class GLSM_LegacyStackQueries_GLTest { GLStateManager.glLight(GL11.GL_LIGHT1, GL11.GL_SPOT_DIRECTION, values); GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_SPOT_CUTOFF, 45.75f); GLStateManager.glLightf(GL11.GL_LIGHT1, GL11.GL_LINEAR_ATTENUATION, 2.5f); - drainErrors(); ints.position(2); GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, ints); @@ -606,7 +429,6 @@ public class GLSM_LegacyStackQueries_GLTest { assertEquals(-777, ints.get(0)); GLStateManager.glGetLight(GL11.GL_LIGHT1, GL11.GL_SHININESS, ints); assertEquals(-777, ints.get(0)); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } finally { GLStateManager.glLoadIdentity(); savedDiffuse.limit(4); @@ -637,7 +459,6 @@ public class GLSM_LegacyStackQueries_GLTest { GLStateManager.glMaterialf(GL11.GL_FRONT, GL11.GL_SHININESS, 12.6f); values.put(0, 2.5f).put(1, -2.5f).put(2, 7.4f).put(3, 0.0f); GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_COLOR_INDEXES, values); - drainErrors(); ints.position(1); GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, ints); @@ -665,7 +486,6 @@ public class GLSM_LegacyStackQueries_GLTest { assertEquals(-777, ints.get(0)); GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_SPOT_CUTOFF, ints); assertEquals(-777, ints.get(0)); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } finally { savedSpecular.limit(4); GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, savedSpecular); @@ -691,7 +511,6 @@ public class GLSM_LegacyStackQueries_GLTest { GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE1, 240.0f, 16.0f); GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE2, 0.75f, 0.125f); GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE3, 0.375f, 0.625f); - drainErrors(); final float[][] expected = { { 0.25f, 0.5f }, { 240.0f, 16.0f }, { 0.75f, 0.125f }, { 0.375f, 0.625f } }; for (int unit = 0; unit < 4; unit++) { @@ -702,7 +521,6 @@ public class GLSM_LegacyStackQueries_GLTest { assertEquals(0.0f, buf.get(2), "r of unit " + unit); assertEquals(1.0f, buf.get(3), "q of unit " + unit); } - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } finally { for (int unit = 0; unit < 4; unit++) { GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0 + unit, saved[unit][0], saved[unit][1]); @@ -719,7 +537,6 @@ public class GLSM_LegacyStackQueries_GLTest { GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.25f, 0.5f); GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + unit); - drainErrors(); for (int i = 0; i < 4; i++) { buf.put(i, -7.0f); } @@ -728,10 +545,481 @@ public class GLSM_LegacyStackQueries_GLTest { assertEquals(0.0f, buf.get(1)); assertEquals(0.0f, buf.get(2)); assertEquals(1.0f, buf.get(3)); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } finally { GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); GLStateManager.glMultiTexCoord2f(GL13.GL_TEXTURE0, 0.0f, 0.0f); } } + + @Test + void legacyQueriesNeverReachTheDriver() { + final int base = GLStateManager.getAttribDepth(); + final IntBuffer ints = BufferUtils.createIntBuffer(16); + final FloatBuffer floats = BufferUtils.createFloatBuffer(16); + final DoubleBuffer doubles = BufferUtils.createDoubleBuffer(16); + final ByteBuffer bytes = BufferUtils.createByteBuffer(16); + final int[] intArray = new int[16]; + final float[] floatArray = new float[16]; + final double[] doubleArray = new double[16]; + try { + GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT); + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_PIXEL_STORE_BIT); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); + assumeDriverRejectsLegacyPnames(); + for (final int pname : LEGACY_PNAMES) { + drainErrors(); + final int scalar = GLStateManager.glGetInteger(pname); + GLStateManager.glGetFloat(pname); + GLStateManager.glGetBoolean(pname); + GLStateManager.glIsEnabled(pname); + ints.clear(); + ints.put(0, -12345); + GLStateManager.glGetInteger(pname, ints); + floats.clear(); + GLStateManager.glGetFloat(pname, floats); + doubles.clear(); + GLStateManager.glGetDouble(pname, doubles); + bytes.clear(); + GLStateManager.glGetBoolean(pname, bytes); + intArray[0] = -12345; + GLStateManager.glGetInteger(pname, intArray); + GLStateManager.glGetFloat(pname, floatArray); + GLStateManager.glGetDouble(pname, doubleArray); + if (LegacyQueries.count(LegacyQueries.desc(pname)) == 1) { + assertEquals(scalar, ints.get(0), "IntBuffer overload for " + hex(pname)); + assertEquals(scalar, intArray[0], "int[] overload for " + hex(pname)); + } + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "driver error after querying " + hex(pname)); + } + } finally { + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + drainClientAttribStack(); + popAttribTo(base); + } + } + + @Test + void typedEntryPointsAgreeUnderMesaConversions() { + final IntBuffer ints = BufferUtils.createIntBuffer(16); + final FloatBuffer floats = BufferUtils.createFloatBuffer(16); + final DoubleBuffer doubles = BufferUtils.createDoubleBuffer(16); + final FloatBuffer savedColor = BufferUtils.createFloatBuffer(16); + final FloatBuffer savedNormal = BufferUtils.createFloatBuffer(16); + GLStateManager.glGetFloat(GL11.GL_CURRENT_COLOR, savedColor); + GLStateManager.glGetFloat(GL11.GL_CURRENT_NORMAL, savedNormal); + final int savedFunc = GLStateManager.glGetInteger(GL11.GL_ALPHA_TEST_FUNC); + final float savedRef = GLStateManager.glGetFloat(GL11.GL_ALPHA_TEST_REF); + final float savedStart = GLStateManager.glGetFloat(GL11.GL_FOG_START); + final float savedEnd = GLStateManager.glGetFloat(GL11.GL_FOG_END); + final float savedDensity = GLStateManager.glGetFloat(GL11.GL_FOG_DENSITY); + final boolean savedLighting = GLStateManager.glIsEnabled(GL11.GL_LIGHTING); + final boolean savedFog = GLStateManager.glIsEnabled(GL11.GL_FOG); + final int savedShade = GLStateManager.glGetInteger(GL11.GL_SHADE_MODEL); + final int savedHint = GLStateManager.glGetInteger(GL11.GL_FOG_HINT); + final float savedMinSize = GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN); + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + GLStateManager.glPushMatrix(); + try { + GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); + GLStateManager.glFogf(GL11.GL_FOG_START, -1.5f); + GLStateManager.glFogf(GL11.GL_FOG_END, 2.5f); + GLStateManager.glFogf(GL11.GL_FOG_DENSITY, 0.25f); + GLStateManager.glColor4f(0.25f, 0.5f, 0.75f, 1f); + GLStateManager.glNormal3f(0f, -0.5f, 1f); + GLStateManager.glEnable(GL11.GL_LIGHTING); + GLStateManager.glEnable(GL11.GL_FOG); + GLStateManager.glShadeModel(GL11.GL_FLAT); + GLStateManager.glHint(GL11.GL_FOG_HINT, GL11.GL_NICEST); + GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, 2.5f); + GLStateManager.glTranslatef(1.5f, -2.5f, 3f); + for (final int pname : LEGACY_PNAMES) { + final int desc = LegacyQueries.desc(pname); + final int type = LegacyQueries.type(desc); + final int count = LegacyQueries.count(desc); + final String name = hex(pname); + if (count == 1) { + if (type == LegacyQueries.BOOL) { + final boolean b = GLStateManager.glGetBoolean(pname); + assertEquals(b ? 1 : 0, GLStateManager.glGetInteger(pname), "int of bool " + name); + assertEquals(b ? 1.0f : 0.0f, GLStateManager.glGetFloat(pname), "float of bool " + name); + } else if (type == LegacyQueries.INT) { + final int i = GLStateManager.glGetInteger(pname); + assertEquals((float) i, GLStateManager.glGetFloat(pname), "float of int " + name); + assertEquals(i != 0, GLStateManager.glGetBoolean(pname), "bool of int " + name); + } else { + final float f = GLStateManager.glGetFloat(pname); + assertEquals(LegacyQueries.toInt(type, f), GLStateManager.glGetInteger(pname), "int of float " + name); + assertEquals(f != 0.0f, GLStateManager.glGetBoolean(pname), "bool of float " + name); + } + } else { + floats.clear(); + GLStateManager.glGetFloat(pname, floats); + ints.clear(); + GLStateManager.glGetInteger(pname, ints); + doubles.clear(); + GLStateManager.glGetDouble(pname, doubles); + for (int i = 0; i < count; i++) { + final float f = floats.get(i); + assertEquals(LegacyQueries.toInt(type, f), ints.get(i), "int element " + i + " of " + name); + assertEquals((double) f, doubles.get(i), "double element " + i + " of " + name); + } + assertEquals(floats.get(0), GLStateManager.glGetFloat(pname), "scalar float of " + name); + } + } + } finally { + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + GLStateManager.glPopMatrix(); + GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, savedMinSize); + GLStateManager.glHint(GL11.GL_FOG_HINT, savedHint); + GLStateManager.glShadeModel(savedShade); + setEnabled(GL11.GL_FOG, savedFog); + setEnabled(GL11.GL_LIGHTING, savedLighting); + GLStateManager.glNormal3f(savedNormal.get(0), savedNormal.get(1), savedNormal.get(2)); + GLStateManager.glColor4f(savedColor.get(0), savedColor.get(1), savedColor.get(2), savedColor.get(3)); + GLStateManager.glFogf(GL11.GL_FOG_DENSITY, savedDensity); + GLStateManager.glFogf(GL11.GL_FOG_END, savedEnd); + GLStateManager.glFogf(GL11.GL_FOG_START, savedStart); + GLStateManager.glAlphaFunc(savedFunc, savedRef); + } + } + + private static float driverMaxPointSize() { + final FloatBuffer range = BufferUtils.createFloatBuffer(16); + GL11.glGetFloat(GL11.GL_POINT_SIZE_RANGE, range); + final float smooth = range.get(1); + // A rejected query leaves the buffer alone, so the smooth maximum stands in for the missing aliased range + GL11.glGetFloat(GL12.GL_ALIASED_POINT_SIZE_RANGE, range); + drainErrors(); + return Math.max(smooth, range.get(1)); + } + + @Test + void edgeFlagIsCompiledIntoDisplayLists() { + final int list = GLStateManager.glGenLists(1); + try { + GLStateManager.glEdgeFlag(true); + GLStateManager.glNewList(list, GL11.GL_COMPILE); + GLStateManager.glEdgeFlag(false); + GLStateManager.glEndList(); + assertTrue(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG), "compiling must not change the live edge flag"); + GLStateManager.glCallList(list); + assertFalse(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG)); + } finally { + GLStateManager.glEdgeFlag(true); + GLStateManager.glDeleteLists(list, 1); + } + } + + @Test + void textureBitRestoresPerUnitTextureEnables() { + final int base = GLStateManager.getAttribDepth(); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + final boolean cube = GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP); + final boolean texture2d = GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D); + try { + GLStateManager.glPushAttrib(GL11.GL_TEXTURE_BIT); + setEnabled(GL13.GL_TEXTURE_CUBE_MAP, !cube); + setEnabled(GL11.GL_TEXTURE_2D, !texture2d); + GLStateManager.glPopAttrib(); + assertEquals(cube, GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP)); + assertEquals(texture2d, GLStateManager.glIsEnabled(GL11.GL_TEXTURE_2D)); + } finally { + popAttribTo(base); + setEnabled(GL13.GL_TEXTURE_CUBE_MAP, cube); + setEnabled(GL11.GL_TEXTURE_2D, texture2d); + } + } + + @Test + void crossTypeQueriesConvert() { + final FloatBuffer savedColor = BufferUtils.createFloatBuffer(16); + GLStateManager.glGetFloat(GL11.GL_CURRENT_COLOR, savedColor); + final int savedFunc = GLStateManager.glGetInteger(GL11.GL_ALPHA_TEST_FUNC); + final float savedRef = GLStateManager.glGetFloat(GL11.GL_ALPHA_TEST_REF); + final float savedStart = GLStateManager.glGetFloat(GL11.GL_FOG_START); + final boolean savedLighting = GLStateManager.glIsEnabled(GL11.GL_LIGHTING); + final IntBuffer ints = BufferUtils.createIntBuffer(16); + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + GLStateManager.glPushMatrix(); + try { + GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); + assertEquals(1073741823, GLStateManager.glGetInteger(GL11.GL_ALPHA_TEST_REF)); + + assertEquals((float) GLStateManager.glGetInteger(GL11.GL_FOG_MODE), GLStateManager.glGetFloat(GL11.GL_FOG_MODE)); + + GLStateManager.glFogf(GL11.GL_FOG_START, -1.5f); + assertEquals(-2, GLStateManager.glGetInteger(GL11.GL_FOG_START)); + + GLStateManager.glEnable(GL11.GL_LIGHTING); + assertEquals(1, GLStateManager.glGetInteger(GL11.GL_LIGHTING)); + GLStateManager.glDisable(GL11.GL_LIGHTING); + assertEquals(0, GLStateManager.glGetInteger(GL11.GL_LIGHTING)); + + GLStateManager.glColor4f(1f, 0.5f, 0f, 1f); + ints.clear(); + GLStateManager.glGetInteger(GL11.GL_CURRENT_COLOR, ints); + assertEquals(2147483647, ints.get(0)); + assertEquals(1073741823, ints.get(1)); + assertEquals(0, ints.get(2)); + assertEquals(2147483647, ints.get(3)); + + GLStateManager.glLoadIdentity(); + GLStateManager.glGetInteger(GL11.GL_MODELVIEW_MATRIX, ints); + assertEquals(2147483647, ints.get(0), "matrix elements scale like normalized floats"); + assertEquals(0, ints.get(1)); + assertEquals(2147483647, ints.get(15)); + assertEquals(2147483647, GLStateManager.glGetInteger(GL11.GL_MODELVIEW_MATRIX)); + assertEquals(1.0f, GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX)); + } finally { + GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); + GLStateManager.glPopMatrix(); + GLStateManager.glFogf(GL11.GL_FOG_START, savedStart); + GLStateManager.glColor4f(savedColor.get(0), savedColor.get(1), savedColor.get(2), savedColor.get(3)); + GLStateManager.glAlphaFunc(savedFunc, savedRef); + setEnabled(GL11.GL_LIGHTING, savedLighting); + } + } + + @Test + void conversionHelpersMatchMesa() { + assertEquals(1, LegacyQueries.lround(0.5f)); + assertEquals(-1, LegacyQueries.lround(-0.5f)); + assertEquals(2, LegacyQueries.lround(2.4f)); + assertEquals(-3, LegacyQueries.lround(-2.6f)); + assertEquals(Integer.MAX_VALUE, LegacyQueries.floatToInt(1f)); + assertEquals(0, LegacyQueries.floatToInt(0f)); + assertEquals(-Integer.MAX_VALUE, LegacyQueries.floatToInt(-1f)); + } + + @Test + void queriesWriteAtBufferPosition() { + final IntBuffer ints = BufferUtils.createIntBuffer(32); + final FloatBuffer floats = BufferUtils.createFloatBuffer(32); + final DoubleBuffer doubles = BufferUtils.createDoubleBuffer(32); + final FloatBuffer reference = BufferUtils.createFloatBuffer(32); + final FloatBuffer savedColor = BufferUtils.createFloatBuffer(16); + GLStateManager.glGetFloat(GL11.GL_CURRENT_COLOR, savedColor); + final float savedStart = GLStateManager.glGetFloat(GL11.GL_FOG_START); + try { + for (int i = 0; i < 32; i++) { + ints.put(i, -777); + floats.put(i, -777f); + doubles.put(i, -777.0); + } + + ints.position(4); + GLStateManager.glGetInteger(GL11.GL_MATRIX_MODE, ints); + assertEquals(GLStateManager.glGetInteger(GL11.GL_MATRIX_MODE), ints.get(4)); + assertEquals(-777, ints.get(0)); + + floats.position(4); + GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, floats); + reference.clear(); + GLStateManager.glGetFloat(GL11.GL_MODELVIEW_MATRIX, reference); + for (int i = 0; i < 16; i++) { + assertEquals(reference.get(i), floats.get(4 + i), "modelview element " + i); + } + assertEquals(-777f, floats.get(0)); + + for (int i = 0; i < 32; i++) { + ints.put(i, -777); + } + GLStateManager.glColor4f(0.25f, 0.5f, 0.75f, 1f); + ints.position(4); + GLStateManager.glGetInteger(GL11.GL_CURRENT_COLOR, ints); + assertEquals(536870911, ints.get(4)); + assertEquals(1073741823, ints.get(5)); + assertEquals(1610612735, ints.get(6)); + assertEquals(2147483647, ints.get(7)); + assertEquals(-777, ints.get(0)); + + GLStateManager.glFogf(GL11.GL_FOG_START, 3.5f); + doubles.position(4); + GLStateManager.glGetDouble(GL11.GL_FOG_START, doubles); + assertEquals(3.5, doubles.get(4)); + assertEquals(-777.0, doubles.get(0)); + + for (int i = 0; i < 32; i++) { + floats.put(i, -777f); + } + floats.position(4); + GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, floats); + reference.clear(); + GLStateManager.glGetMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, reference); + for (int i = 0; i < 4; i++) { + assertEquals(reference.get(i), floats.get(4 + i), "diffuse element " + i); + } + assertEquals(-777f, floats.get(0)); + } finally { + GLStateManager.glFogf(GL11.GL_FOG_START, savedStart); + GLStateManager.glColor4f(savedColor.get(0), savedColor.get(1), savedColor.get(2), savedColor.get(3)); + } + } + + @Test + void hintsRoundTripAndFollowHintBit() { + final int base = GLStateManager.getAttribDepth(); + final int[] saved = new int[HINT_TARGETS.length]; + for (int i = 0; i < HINT_TARGETS.length; i++) { + saved[i] = GLStateManager.glGetInteger(HINT_TARGETS[i]); + } + try { + for (final int target : HINT_TARGETS) { + final String name = hex(target); + assertEquals(GL11.GL_DONT_CARE, GLStateManager.glGetInteger(target), "default " + name); + GLStateManager.glHint(target, GL11.GL_NICEST); + assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "nicest " + name); + GLStateManager.glHint(target, 12345); + assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "invalid mode " + name); + } + GLStateManager.glPushAttrib(GL11.GL_HINT_BIT); + for (final int target : HINT_TARGETS) { + GLStateManager.glHint(target, GL11.GL_FASTEST); + assertEquals(GL11.GL_FASTEST, GLStateManager.glGetInteger(target), "fastest " + hex(target)); + } + GLStateManager.glPopAttrib(); + for (final int target : HINT_TARGETS) { + assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "restored " + hex(target)); + } + assertEquals(GL11.GL_RENDER, GLStateManager.glGetInteger(GL11.GL_RENDER_MODE)); + } finally { + popAttribTo(base); + for (int i = 0; i < HINT_TARGETS.length; i++) { + GLStateManager.glHint(HINT_TARGETS[i], saved[i]); + } + } + } + + @Test + void pointParametersRoundTripAndFollowPointBit() { + final int base = GLStateManager.getAttribDepth(); + final FloatBuffer attenuation = BufferUtils.createFloatBuffer(16); + final FloatBuffer savedAttenuation = BufferUtils.createFloatBuffer(16); + final float savedMin = GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN); + final float savedMax = GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX); + final float savedFade = GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE); + final int savedOrigin = GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN); + GLStateManager.glGetFloat(GL14.GL_POINT_DISTANCE_ATTENUATION, savedAttenuation); + GLStateManager.glPushAttrib(GL11.GL_POINT_BIT); + try { + assertEquals(0f, savedMin); + assertEquals(driverMaxPointSize(), savedMax); + assertEquals(1f, savedFade); + assertEquals(1f, savedAttenuation.get(0)); + assertEquals(0f, savedAttenuation.get(1)); + assertEquals(0f, savedAttenuation.get(2)); + assertEquals(GL20.GL_UPPER_LEFT, savedOrigin); + + GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, 0.5f); + GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MAX, 2.5f); + GLStateManager.glPointParameterf(GL14.GL_POINT_FADE_THRESHOLD_SIZE, 4.5f); + assertEquals(0.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN)); + assertEquals(2.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX)); + assertEquals(4.5f, GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE)); + GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, -1f); + assertEquals(0.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN)); + + attenuation.put(0, 0.5f).put(1, 0.25f).put(2, 0.125f); + attenuation.position(0).limit(3); + GLStateManager.glPointParameter(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation); + attenuation.clear(); + for (int i = 0; i < 16; i++) { + attenuation.put(i, -777f); + } + GLStateManager.glGetFloat(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation); + assertEquals(0.5f, attenuation.get(0)); + assertEquals(0.25f, attenuation.get(1)); + assertEquals(0.125f, attenuation.get(2)); + + GLStateManager.glPointParameteri(GL20.GL_POINT_SPRITE_COORD_ORIGIN, GL20.GL_LOWER_LEFT); + assertEquals(GL20.GL_LOWER_LEFT, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN)); + + GLStateManager.glPushAttrib(GL11.GL_POINT_BIT); + GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, 1f); + GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MAX, 3f); + GLStateManager.glPointParameterf(GL14.GL_POINT_FADE_THRESHOLD_SIZE, 2f); + attenuation.put(0, 1f).put(1, 1f).put(2, 1f); + attenuation.position(0).limit(3); + GLStateManager.glPointParameter(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation); + GLStateManager.glPointParameteri(GL20.GL_POINT_SPRITE_COORD_ORIGIN, GL20.GL_UPPER_LEFT); + assertEquals(1f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN)); + assertEquals(GL20.GL_UPPER_LEFT, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN)); + GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, Float.NaN); + assertTrue(Float.isNaN(GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN))); + GLStateManager.glPopAttrib(); + + assertEquals(0.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN)); + assertEquals(2.5f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX)); + assertEquals(4.5f, GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE)); + assertEquals(GL20.GL_LOWER_LEFT, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN)); + attenuation.clear(); + GLStateManager.glGetFloat(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation); + assertEquals(0.5f, attenuation.get(0)); + assertEquals(0.25f, attenuation.get(1)); + assertEquals(0.125f, attenuation.get(2)); + } finally { + popAttribTo(base); + assertEquals(savedMax, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX)); + } + } + + @Test + void cacheOnlyEnablesRoundTrip() { + final int base = GLStateManager.getAttribDepth(); + final boolean savedSprite = GLStateManager.glIsEnabled(GL20.GL_POINT_SPRITE); + final boolean savedTwoSide = GLStateManager.glIsEnabled(GL20.GL_VERTEX_PROGRAM_TWO_SIDE); + final boolean savedEdge = GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + final boolean savedCube0 = GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + final boolean savedCube1 = GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + try { + assertEnableRoundTrip(GL20.GL_POINT_SPRITE, false); + assertEnableRoundTrip(GL20.GL_VERTEX_PROGRAM_TWO_SIDE, false); + assertEnableRoundTrip(GL13.GL_TEXTURE_CUBE_MAP, false); + + GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT); + GLStateManager.glEnable(GL20.GL_POINT_SPRITE); + GLStateManager.glEnable(GL20.GL_VERTEX_PROGRAM_TWO_SIDE); + GLStateManager.glEnable(GL13.GL_TEXTURE_CUBE_MAP); + assertTrue(GLStateManager.glIsEnabled(GL20.GL_POINT_SPRITE)); + assertTrue(GLStateManager.glIsEnabled(GL20.GL_VERTEX_PROGRAM_TWO_SIDE)); + assertTrue(GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP)); + GLStateManager.glPopAttrib(); + assertFalse(GLStateManager.glIsEnabled(GL20.GL_POINT_SPRITE)); + assertFalse(GLStateManager.glIsEnabled(GL20.GL_VERTEX_PROGRAM_TWO_SIDE)); + assertFalse(GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP)); + + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.glDisable(GL13.GL_TEXTURE_CUBE_MAP); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glEnable(GL13.GL_TEXTURE_CUBE_MAP); + assertTrue(GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP)); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + assertFalse(GLStateManager.glIsEnabled(GL13.GL_TEXTURE_CUBE_MAP)); + assertFalse(GLStateManager.glGetBoolean(GL13.GL_TEXTURE_CUBE_MAP)); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + assertTrue(GLStateManager.glGetBoolean(GL13.GL_TEXTURE_CUBE_MAP)); + GLStateManager.glDisable(GL13.GL_TEXTURE_CUBE_MAP); + + assertTrue(savedEdge); + GLStateManager.glEdgeFlag(false); + assertFalse(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG)); + GLStateManager.glPushAttrib(GL11.GL_CURRENT_BIT); + GLStateManager.glEdgeFlag(true); + assertTrue(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG)); + GLStateManager.glPopAttrib(); + assertFalse(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG)); + } finally { + popAttribTo(base); + GLStateManager.glEdgeFlag(savedEdge); + setEnabled(GL20.GL_POINT_SPRITE, savedSprite); + setEnabled(GL20.GL_VERTEX_PROGRAM_TWO_SIDE, savedTwoSide); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + setEnabled(GL13.GL_TEXTURE_CUBE_MAP, savedCube1); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + setEnabled(GL13.GL_TEXTURE_CUBE_MAP, savedCube0); + } + } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_Lighting_UnitTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_Lighting_UnitTest.java index 9feca4617..1b5b713c0 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_Lighting_UnitTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_Lighting_UnitTest.java @@ -4,10 +4,7 @@ import org.junit.jupiter.api.Test; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.floats; import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyLightState; import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyMaterialState; import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState; @@ -15,9 +12,6 @@ import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState; @GLCompatTest public class GLSM_Lighting_UnitTest { - static final FloatBuffer f4b = ByteBuffer.allocateDirect(4 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer(); - static final FloatBuffer f3b = ByteBuffer.allocateDirect(3 << 2).order(ByteOrder.nativeOrder()).asFloatBuffer(); - @Test void testLightEnablement() { verifyState(GL11.GL_LIGHT0, false, "GL_LIGHT0 Initial State"); @@ -46,12 +40,10 @@ public class GLSM_Lighting_UnitTest { verifyLightState(GL11.GL_LIGHT1, GL11.GL_SPECULAR, new float[]{0F, 0F, 0F, 1F}, "GL_LIGHT1 Initial Specular"); verifyLightState(GL11.GL_LIGHT1, GL11.GL_POSITION, new float[]{0F, 0F, 1F, 0F}, "GL_LIGHT1 Initial Position"); - newf4b(0.25F, 0.5F, 0.25F, 0.5F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, floats(0.25F, 0.5F, 0.25F, 0.5F)); verifyLightState(GL11.GL_LIGHT0, GL11.GL_AMBIENT, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 Ambient Changed State"); GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT); - newf4b(0.3F, 0.4F, 0.5F, 0.6F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, floats(0.3F, 0.4F, 0.5F, 0.6F)); verifyLightState(GL11.GL_LIGHT0, GL11.GL_AMBIENT, new float[]{0.3F, 0.4F, 0.5F, 0.6F}, "GL_LIGHT0 attrib push state changed check"); GLStateManager.glPopAttrib(); verifyLightState(GL11.GL_LIGHT0, GL11.GL_AMBIENT, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 attrib pop state check"); @@ -64,33 +56,27 @@ public class GLSM_Lighting_UnitTest { GLStateManager.glPushMatrix(); GLStateManager.glTranslatef(25.0F, 25.0F, 25.0F); GLStateManager.glRotatef(35.0F, 10.0F, 15.0F, 20.0F); - newf4b(0.25F, 0.5F, 0.25F, 0.0F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.25F, 0.5F, 0.25F, 0.0F)); verifyLightState(GL11.GL_LIGHT0, GL11.GL_POSITION, new float[]{0.1090F, 0.5189F, 0.3062F, 0.0F}, "GL_LIGHT0 Position Changed State"); GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT); - newf4b(0.3F, 0.4F, 0.5F, 0.0F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, floats(0.3F, 0.4F, 0.5F, 0.0F)); verifyLightState(GL11.GL_LIGHT0, GL11.GL_POSITION, new float[]{0.2824F, 0.4200F, 0.4937F, 0.0F}, "GL_LIGHT0 attrib push state changed check"); GLStateManager.glPopAttrib(); verifyLightState(GL11.GL_LIGHT0, GL11.GL_POSITION, new float[]{0.1090F, 0.5189F, 0.3062F, 0.0F}, "GL_LIGHT0 attrib pop state check"); GLStateManager.glPopMatrix(); - newf4b(0.25F, 0.5F, 0.25F, 0.5F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, floats(0.25F, 0.5F, 0.25F, 0.5F)); verifyLightState(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 Diffuse Changed State"); GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT); - newf4b(0.3F, 0.4F, 0.5F, 0.6F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, floats(0.3F, 0.4F, 0.5F, 0.6F)); verifyLightState(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, new float[]{0.3F, 0.4F, 0.5F, 0.6F}, "GL_LIGHT0 attrib push state changed check"); GLStateManager.glPopAttrib(); verifyLightState(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 attrib pop state check"); - newf4b(0.25F, 0.5F, 0.25F, 0.5F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, floats(0.25F, 0.5F, 0.25F, 0.5F)); verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPECULAR, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 Specular Changed State"); GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT); - newf4b(0.3F, 0.4F, 0.5F, 0.6F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, floats(0.3F, 0.4F, 0.5F, 0.6F)); verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPECULAR, new float[]{0.3F, 0.4F, 0.5F, 0.6F}, "GL_LIGHT0 attrib push state changed check"); GLStateManager.glPopAttrib(); verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPECULAR, new float[]{0.25F, 0.5F, 0.25F, 0.5F}, "GL_LIGHT0 attrib pop state check"); @@ -99,12 +85,10 @@ public class GLSM_Lighting_UnitTest { GLStateManager.glPushMatrix(); GLStateManager.glTranslatef(25.0F, 25.0F, 25.0F); GLStateManager.glRotatef(35.0F, 10.0F, 15.0F, 20.0F); - newf4b(0.25F, 0.5F, 0.25F, 0.0F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.25F, 0.5F, 0.25F, 0.0F)); verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, new float[]{0.1090F, 0.5189F, 0.3062F}, "GL_LIGHT0 Spot Direction Changed State"); GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT); - newf4b(0.3F, 0.4F, 0.5F, 0.0F); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, f4b); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, floats(0.3F, 0.4F, 0.5F, 0.0F)); verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, new float[]{0.2824F, 0.4200F, 0.4937F}, "GL_LIGHT0 attrib push state changed check"); GLStateManager.glPopAttrib(); verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, new float[]{0.1090F, 0.5189F, 0.3062F}, "GL_LIGHT0 attrib pop state check"); @@ -157,8 +141,7 @@ public class GLSM_Lighting_UnitTest { void testLightModel() { verifyState(GL11.GL_LIGHT_MODEL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F}, "GL_LIGHT_MODEL_AMBIENT initial state"); GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT); - newf4b(0.8F, 0.6F, 0.4F, 0.2F); - GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, f4b); + GLStateManager.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, floats(0.8F, 0.6F, 0.4F, 0.2F)); verifyState(GL11.GL_LIGHT_MODEL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, "GL_LIGHT_MODEL_AMBIENT changed state"); GLStateManager.glPopAttrib(); verifyState(GL11.GL_LIGHT_MODEL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F}); @@ -235,16 +218,14 @@ public class GLSM_Lighting_UnitTest { void testMaterial() { verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F}, ""); GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT); - newf4b(0.8F, 0.6F, 0.4F, 0.2F); - GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, f4b); + GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, floats(0.8F, 0.6F, 0.4F, 0.2F)); verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, ""); GLStateManager.glPopAttrib(); verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F}, ""); verifyMaterialState(GL11.GL_BACK, GL11.GL_AMBIENT, new float[]{0.2F, 0.2F, 0.2F, 1.0F}, ""); GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT); - newf4b(0.8F, 0.6F, 0.4F, 0.2F); - GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT, f4b); + GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT, floats(0.8F, 0.6F, 0.4F, 0.2F)); verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, ""); verifyMaterialState(GL11.GL_BACK, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, ""); GLStateManager.glPopAttrib(); @@ -254,8 +235,7 @@ public class GLSM_Lighting_UnitTest { verifyMaterialState(GL11.GL_FRONT, GL11.GL_DIFFUSE, new float[]{0.8F, 0.8F, 0.8F, 1.0F}, ""); verifyMaterialState(GL11.GL_BACK, GL11.GL_DIFFUSE, new float[]{0.8F, 0.8F, 0.8F, 1.0F}, ""); GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT); - newf4b(0.8F, 0.6F, 0.4F, 0.2F); - GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE, f4b); + GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE, floats(0.8F, 0.6F, 0.4F, 0.2F)); verifyMaterialState(GL11.GL_FRONT, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, ""); verifyMaterialState(GL11.GL_BACK, GL11.GL_AMBIENT, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, ""); verifyMaterialState(GL11.GL_FRONT, GL11.GL_DIFFUSE, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, ""); @@ -268,16 +248,14 @@ public class GLSM_Lighting_UnitTest { verifyMaterialState(GL11.GL_BACK, GL11.GL_SPECULAR, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, ""); GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT); - newf4b(0.8F, 0.6F, 0.4F, 0.2F); - GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_SPECULAR, f4b); + GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_SPECULAR, floats(0.8F, 0.6F, 0.4F, 0.2F)); verifyMaterialState(GL11.GL_BACK, GL11.GL_SPECULAR, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, ""); GLStateManager.glPopAttrib(); verifyMaterialState(GL11.GL_BACK, GL11.GL_SPECULAR, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, ""); verifyMaterialState(GL11.GL_BACK, GL11.GL_EMISSION, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, ""); GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT); - newf4b(0.8F, 0.6F, 0.4F, 0.2F); - GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_EMISSION, f4b); + GLStateManager.glMaterial(GL11.GL_BACK, GL11.GL_EMISSION, floats(0.8F, 0.6F, 0.4F, 0.2F)); verifyMaterialState(GL11.GL_BACK, GL11.GL_EMISSION, new float[]{0.8F, 0.6F, 0.4F, 0.2F}, ""); GLStateManager.glPopAttrib(); verifyMaterialState(GL11.GL_BACK, GL11.GL_EMISSION, new float[]{0.0F, 0.0F, 0.0F, 1.0F}, ""); @@ -291,8 +269,7 @@ public class GLSM_Lighting_UnitTest { verifyMaterialState(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, new float[]{0.0F, 1.0F, 1.0F}, ""); GLStateManager.glPushAttrib(GL11.GL_LIGHTING_BIT); - newf4b(0.8F, 0.6F, 0.4F, 0.0F); - GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, f4b); + GLStateManager.glMaterial(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, floats(0.8F, 0.6F, 0.4F, 0.0F)); if (GLStateManager.vendorIsNVIDIA()) { verifyMaterialState(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, new float[]{0.8F, 0.4F, 0.6F}, ""); } else { @@ -302,16 +279,4 @@ public class GLSM_Lighting_UnitTest { verifyMaterialState(GL11.GL_FRONT, GL11.GL_COLOR_INDEXES, new float[]{0.0F, 1.0F, 1.0F}, ""); } - - static void newf4b(float x, float y, float z, float w) { - f4b.clear(); - f4b.put(x).put(y).put(z).put(w); - f4b.flip(); - } - - static void newf3b(float x, float y, float z) { - f3b.clear(); - f3b.put(x).put(y).put(z); - f3b.flip(); - } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_LineStipple_GLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_LineStipple_GLTest.java index 80adc37b9..4115eb40d 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_LineStipple_GLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_LineStipple_GLTest.java @@ -5,6 +5,9 @@ import com.gtnewhorizons.angelica.glsm.states.ViewportState; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; @@ -12,6 +15,7 @@ import org.lwjgl.opengl.GL15; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.function.IntPredicate; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -72,7 +76,6 @@ public class GLSM_LineStipple_GLTest { GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); if (vbo != 0) GLStateManager.glDeleteBuffers(vbo); if (vao != 0) GLStateManager.glDeleteVertexArrays(vao); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} } private boolean[] renderLine(float x0, float x1) { @@ -117,35 +120,29 @@ public class GLSM_LineStipple_GLTest { GLStateManager.glLineStipple(factor, (short) pattern); } - @Test - void solidPatternCoversEveryPixel() { - stipple(1, 0xFFFF); - assertArrayEquals(expected(i -> true), renderLine(0f, W)); + static Stream patterns() { + return Stream.of( + pattern("solid pattern covers every pixel", true, 1, 0xFFFF, i -> true), + pattern("empty pattern covers nothing", true, 1, 0x0000, i -> false), + pattern("alternating pattern dashes every other pixel", true, 1, 0xAAAA, i -> (i & 1) == 1), + pattern("factor stretches each bit across that many pixels", true, 2, 0xAAAA, i -> ((i / 2) & 1) == 1), + pattern("disabled stipple covers every pixel", false, 1, 0xAAAA, i -> true)); } - @Test - void emptyPatternCoversNothing() { - stipple(1, 0x0000); - assertArrayEquals(expected(i -> false), renderLine(0f, W)); + private static Arguments pattern(String name, boolean enabled, int factor, int pattern, IntPredicate on) { + return Arguments.of(name, enabled, factor, pattern, on); } - @Test - void alternatingPatternDashesEveryOtherPixel() { - stipple(1, 0xAAAA); - assertArrayEquals(expected(i -> (i & 1) == 1), renderLine(0f, W)); - } - - @Test - void factorStretchesEachBitAcrossThatManyPixels() { - stipple(2, 0xAAAA); - assertArrayEquals(expected(i -> ((i / 2) & 1) == 1), renderLine(0f, W)); - } - - @Test - void disabledStippleCoversEveryPixel() { - GLStateManager.glLineStipple(1, (short) 0xAAAA); - GLStateManager.glDisable(GL11.GL_LINE_STIPPLE); - assertArrayEquals(expected(i -> true), renderLine(0f, W)); + @ParameterizedTest(name = "{0}") + @MethodSource("patterns") + void patternSelectsTheCoveredPixels(String name, boolean enabled, int factor, int pattern, IntPredicate on) { + GLStateManager.glLineStipple(factor, (short) pattern); + if (enabled) { + GLStateManager.glEnable(GL11.GL_LINE_STIPPLE); + } else { + GLStateManager.glDisable(GL11.GL_LINE_STIPPLE); + } + assertArrayEquals(expected(on), renderLine(0f, W)); } @Test diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_PerThread_UnitTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_PerThread_UnitTest.java index adb690c69..bc54b7809 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_PerThread_UnitTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_PerThread_UnitTest.java @@ -1,5 +1,6 @@ package com.gtnewhorizons.angelica.glsm; +import com.gtnewhorizons.angelica.glsm.testutil.TestThreads; import org.joml.Matrix4f; import org.junit.jupiter.api.Test; import org.lwjgl.opengl.GL11; @@ -18,23 +19,14 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @GLCoreTest public class GLSM_PerThread_UnitTest { - private static void runThreaded(int n, ThreadBody body) throws Throwable { + private static void runThreaded(int n, ThreadBody body) throws InterruptedException { final CyclicBarrier barrier = new CyclicBarrier(n); - final Throwable[] errs = new Throwable[n]; - final Thread[] ts = new Thread[n]; + final TestThreads.Worker[] workers = new TestThreads.Worker[n]; for (int i = 0; i < n; i++) { final int tid = i; - ts[i] = new Thread(() -> { - try { - body.run(tid, barrier); - } catch (Throwable t) { - errs[tid] = t; - } - }, "glsm-perthread-" + i); + workers[i] = TestThreads.start("glsm-perthread-" + i, () -> body.run(tid, barrier)); } - for (Thread t : ts) t.start(); - for (Thread t : ts) t.join(); - for (int i = 0; i < n; i++) if (errs[i] != null) throw errs[i]; + for (TestThreads.Worker worker : workers) worker.join(); } interface ThreadBody { diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_PushPop_UnitTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_PushPop_UnitTest.java index 72c9251dd..095cea57e 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_PushPop_UnitTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_PushPop_UnitTest.java @@ -13,6 +13,7 @@ import org.lwjgl.opengl.GL20; import java.nio.FloatBuffer; import java.util.ArrayList; +import java.util.List; import static com.gtnewhorizons.angelica.util.GLSMUtil.resetGLState; import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyIsEnabled; @@ -49,6 +50,24 @@ class GLSM_PushPop_UnitTest { public static final float[] FLOAT_ARRAY_3_1 = { 1f, 1f, 1f }; public static final float[] FLOAT_ARRAY_GL_SPOT_DIRECTION_DEFAULT = { 0f, 0f, -1f }; + private static void toggleAndVerify(List bits) { + for (GLBit bit : bits) { + verifyState(bit.glEnum(), bit.initial(), bit.name() + " Initial State"); + if (bit.initial()) { + GLStateManager.glDisable(bit.glEnum()); + } else { + GLStateManager.glEnable(bit.glEnum()); + } + verifyState(bit.glEnum(), !bit.initial(), bit.name() + " Toggle State"); + } + } + + private static void verifyReset(List bits) { + for (GLBit bit : bits) { + verifyState(bit.glEnum(), bit.initial(), bit.name() + " Reset State"); + } + } + @Test void testPushPopColorBufferBit() { @@ -241,19 +260,11 @@ class GLSM_PushPop_UnitTest { bits.add(new GLBit(GL11.GL_TEXTURE_GEN_Q, "Texture Gen Q", false)); GLStateManager.glPushAttrib(GL11.GL_ENABLE_BIT); - bits.forEach(bit -> { - verifyState(bit.glEnum(), bit.initial(), bit.name() + " Initial State"); - if(bit.initial()) { - GLStateManager.glDisable(bit.glEnum()); - } else { - GLStateManager.glEnable(bit.glEnum()); - } - verifyState(bit.glEnum(), !bit.initial(), bit.name() + " Toggle State"); - }); + toggleAndVerify(bits); GLStateManager.glPopAttrib(); - bits.forEach(bit -> verifyState(bit.glEnum(), bit.initial(), bit.name() + " Reset State")); + verifyReset(bits); } @Test @@ -329,15 +340,7 @@ class GLSM_PushPop_UnitTest { verifyState(GL11.GL_SHADE_MODEL, GL11.GL_FLAT, "Shade Model"); - bits.forEach(bit -> { - verifyState(bit.glEnum(), bit.initial(), bit.name() + " Initial State"); - if(bit.initial()) { - GLStateManager.glDisable(bit.glEnum()); - } else { - GLStateManager.glEnable(bit.glEnum()); - } - verifyState(bit.glEnum(), !bit.initial(), bit.name() + " Toggle State"); - }); + toggleAndVerify(bits); GLStateManager.glPopAttrib(); verifyState(GL11.GL_COLOR_MATERIAL_FACE, GL11.GL_FRONT_AND_BACK, "Color Material Face - Reset"); @@ -353,7 +356,7 @@ class GLSM_PushPop_UnitTest { verifyLightState(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, FLOAT_ARRAY_GL_SPOT_DIRECTION_DEFAULT, "Light Spot Direction - Reset"); verifyState(GL11.GL_SHADE_MODEL, GL11.GL_SMOOTH, "Shade Model - Reset"); - bits.forEach(bit -> verifyState(bit.glEnum(), bit.initial(), bit.name() + " Reset State")); + verifyReset(bits); } @Test @@ -388,15 +391,7 @@ class GLSM_PushPop_UnitTest { verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE1, "Active Texture"); verifyState(GL11.GL_TEXTURE_BINDING_2D, tex2, "Texture Binding - Unit 1"); - bits.forEach(bit -> { - verifyState(bit.glEnum(), bit.initial(), bit.name() + " Initial State"); - if(bit.initial()) { - GLStateManager.glDisable(bit.glEnum()); - } else { - GLStateManager.glEnable(bit.glEnum()); - } - verifyState(bit.glEnum(), !bit.initial(), bit.name() + " Toggle State"); - }); + toggleAndVerify(bits); GLStateManager.glPopAttrib(); verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE0, "Active Texture Unit 0 - Reset"); @@ -404,7 +399,7 @@ class GLSM_PushPop_UnitTest { GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); verifyState(GL11.GL_TEXTURE_BINDING_2D, 0, "Texture Binding Unit 1 - Reset"); - bits.forEach(bit -> verifyState(bit.glEnum(), bit.initial(), bit.name() + " Reset State")); + verifyReset(bits); GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); GLStateManager.glDeleteTextures(tex1); @@ -1156,30 +1151,6 @@ class GLSM_PushPop_UnitTest { GLStateManager.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO); } - @Test - void testPolygonStateReadBack() { - GLStateManager.glCullFace(GL11.GL_FRONT); - GLStateManager.glFrontFace(GL11.GL_CW); - GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE); - - verifyState(GL11.GL_CULL_FACE_MODE, GL11.GL_FRONT, "Cull Face Mode"); - verifyState(GL11.GL_FRONT_FACE, GL11.GL_CW, "Front Face"); - verifyState(GL11.GL_POLYGON_MODE, new int[] { GL11.GL_LINE, GL11.GL_LINE }, "Polygon Mode"); - - GLStateManager.glCullFace(GL11.GL_BACK); - GLStateManager.glFrontFace(GL11.GL_CCW); - GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL); - } - - @Test - void testActiveTextureReadBack() { - GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); - verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE1, "Active Texture"); - - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - verifyState(GL13.GL_ACTIVE_TEXTURE, GL13.GL_TEXTURE0, "Active Texture - Reset"); - } - @Test void testUntouchedPushPopRoundTripLeavesStateIdentical() { GLStateManager.glDepthFunc(GL11.GL_LEQUAL); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_RetainedState_GLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_RetainedState_GLTest.java index de2ce97d0..f5921ab8e 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_RetainedState_GLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_RetainedState_GLTest.java @@ -6,6 +6,8 @@ import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL14; +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors; +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.driverTextureBinding; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -15,14 +17,6 @@ public class GLSM_RetainedState_GLTest { private static final StateSet ALL = StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS); - private static int driverTextureBinding(int unit) { - final int saved = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE); - GL13.glActiveTexture(GL13.GL_TEXTURE0 + unit); - final int binding = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D); - GL13.glActiveTexture(saved); - return binding; - } - private static void baseline() { GLStateManager.enableCull(); GLStateManager.disableBlend(); @@ -234,7 +228,7 @@ public class GLSM_RetainedState_GLTest { @Test void aDeletedProgramIsNotRebound() { final RetainedState retained = new RetainedState(); - final int program = GLSM_StateSet_GLTest.linkTrivialProgram(); + final int program = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS); try { baseline(); final int d = GLStateManager.pushState(ALL); @@ -243,11 +237,10 @@ public class GLSM_RetainedState_GLTest { GLStateManager.popStateTo(d); GLStateManager.glUseProgram(0); GLStateManager.glDeleteProgram(program); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + drainErrors(); GLStateManager.applyRetainedState(retained); assertEquals(0, GLStateManager.getActiveProgram()); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } finally { GLStateManager.glUseProgram(0); baseline(); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_SamplerBinding_GLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_SamplerBinding_GLTest.java index 51165681c..ed5b2fa8c 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_SamplerBinding_GLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_SamplerBinding_GLTest.java @@ -24,7 +24,6 @@ class GLSM_SamplerBinding_GLTest { @BeforeEach void requireSamplerObjects() { assumeTrue(RenderSystem.supportsSamplerObjects()); - drainGlErrors(); } @AfterEach @@ -35,11 +34,6 @@ class GLSM_SamplerBinding_GLTest { GL33.glBindSampler(unit, 0); } GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - drainGlErrors(); - } - - private static void drainGlErrors() { - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} } private static int driverBinding(int unit) { diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ServerSideState_UnitTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ServerSideState_UnitTest.java index 2ee9dbe65..0d3d98567 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ServerSideState_UnitTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ServerSideState_UnitTest.java @@ -3,6 +3,8 @@ package com.gtnewhorizons.angelica.glsm; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.LWJGLException; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.GL11; @@ -12,6 +14,7 @@ import org.lwjgl.opengl.SharedDrawable; import java.nio.ByteBuffer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -43,21 +46,13 @@ public class GLSM_ServerSideState_UnitTest { private boolean originalSplashComplete; private Thread originalDrawableGLHolder; - private static void setSplashComplete(boolean value) { - SplashWindow.setSplashComplete(value); - } - - private static boolean getSplashComplete() { - return SplashWindow.isSplashComplete(); - } - @BeforeEach void setupSharedDrawable() throws Exception { - originalSplashComplete = getSplashComplete(); + originalSplashComplete = SplashWindow.isSplashComplete(); originalDrawableGLHolder = GLStateManager.getDrawableGLHolder(); // Reset splash state to simulate splash screen scenario - setSplashComplete(false); + SplashWindow.setSplashComplete(false); sharedDrawable = new SharedDrawable(Display.getDrawable()); @@ -149,7 +144,7 @@ public class GLSM_ServerSideState_UnitTest { // Restore original state GLStateManager.setDrawableGL(null); GLStateManager.setDrawableGLHolder(originalDrawableGLHolder); - setSplashComplete(originalSplashComplete); + SplashWindow.setSplashComplete(originalSplashComplete); if (originalSplashComplete) { GLStateManager.markSplashComplete("test"); } @@ -214,46 +209,81 @@ public class GLSM_ServerSideState_UnitTest { GL11.glDeleteTextures(texShared[0]); } + /** One texture parameter through its typed GLSM setter and getter, plus the raw driver read. Values travel as float. */ + private static final class TexParam { + final String name; + final float initial; + final float changed; + final FloatConsumer set; + final FloatSupplier glsm; + final FloatSupplier driver; + + TexParam(String name, float initial, float changed, FloatConsumer set, FloatSupplier glsm, FloatSupplier driver) { + this.name = name; + this.initial = initial; + this.changed = changed; + this.set = set; + this.glsm = glsm; + this.driver = driver; + } + + @Override + public String toString() { + return name; + } + } + + private interface FloatConsumer { + void accept(float value); + } + + private interface FloatSupplier { + float get(); + } + + static Stream texParams() { + return Stream.of( + new TexParam("glTexParameteri MIN_FILTER", GL11.GL_LINEAR, GL11.GL_NEAREST, + v -> GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, (int) v), + () -> GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER), + () -> GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER)), + new TexParam("glTexParameterf LOD_BIAS", 0.0f, 1.5f, + v -> GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, v), + () -> GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS), + () -> GL11.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS))); + } + /** - * Test: SharedDrawable sets texture MIN_FILTER, verify both GLSM and actual GL state are correct on both contexts. + * Test: SharedDrawable sets a texture parameter, verify both GLSM and actual GL state are correct on both contexts. */ - @Test - void testTexParameterFromSharedDrawable() throws InterruptedException { + @ParameterizedTest + @MethodSource("texParams") + void testTexParameterFromSharedDrawable(TexParam param) throws InterruptedException { int texId = GL11.glGenTextures(); try { GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId); - GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); + param.set.accept(param.initial); - int initialGLSM = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER); - assertEquals(GL11.GL_LINEAR, initialGLSM, "Initial GLSM should be GL_LINEAR"); + assertEquals(param.initial, param.glsm.get(), 0.001f, "Initial GLSM value"); + assertEquals(param.initial, param.driver.get(), 0.001f, "Initial GL value"); - int initialGL = GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER); - assertEquals(GL11.GL_LINEAR, initialGL, "Initial GL should be GL_LINEAR"); - - // SharedDrawable: bind and change params to GL_NEAREST via GLSM executeOnSharedDrawable(() -> { GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId); int boundTex = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D); assertEquals(texId, boundTex, "SharedDrawable should have texId bound"); - GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); + param.set.accept(param.changed); - int glsmValue = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER); - assertEquals(GL11.GL_NEAREST, glsmValue, "SharedDrawable GLSM should return GL_NEAREST"); - - int glValue = GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER); - assertEquals(GL11.GL_NEAREST, glValue, "SharedDrawable GL should be GL_NEAREST"); + assertEquals(param.changed, param.glsm.get(), 0.001f, "SharedDrawable GLSM value"); + assertEquals(param.changed, param.driver.get(), 0.001f, "SharedDrawable GL value"); }); GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId); - int mainGLSM = GLStateManager.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER); - assertEquals(GL11.GL_NEAREST, mainGLSM, "Main thread GLSM should return GL_NEAREST"); - - int mainGL = GL11.glGetTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER); - assertEquals(GL11.GL_NEAREST, mainGL, "Main thread GL should be GL_NEAREST"); + assertEquals(param.changed, param.glsm.get(), 0.001f, "Main thread GLSM value"); + assertEquals(param.changed, param.driver.get(), 0.001f, "Main thread GL value"); } finally { GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0); @@ -358,50 +388,6 @@ public class GLSM_ServerSideState_UnitTest { } } - /** - * Test glTexParameterf (float parameters like LOD_BIAS). - */ - @Test - void testTexParameterfFromSharedDrawable() throws InterruptedException { - int texId = GL11.glGenTextures(); - - try { - GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId); - GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, 0.0f); - - float initialGLSM = GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS); - float initialGL = GL11.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS); - assertEquals(0.0f, initialGLSM, 0.001f, "Initial GLSM LOD_BIAS should be 0.0"); - assertEquals(0.0f, initialGL, 0.001f, "Initial GL LOD_BIAS should be 0.0"); - - // SharedDrawable: change LOD_BIAS - executeOnSharedDrawable(() -> { - GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId); - - int bound = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D); - assertEquals(texId, bound, "SharedDrawable should have texId bound"); - - GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, 1.5f); - - float glsmValue = GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS); - assertEquals(1.5f, glsmValue, 0.001f, "SharedDrawable GLSM should return 1.5"); - float glValue = GL11.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS); - assertEquals(1.5f, glValue, 0.001f, "SharedDrawable GL should be 1.5"); - }); - - GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texId); - - float finalGLSM = GLStateManager.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS); - float finalGL = GL11.glGetTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS); - assertEquals(1.5f, finalGLSM, 0.001f, "Main thread GLSM should return 1.5"); - assertEquals(1.5f, finalGL, 0.001f, "Main thread GL should be 1.5"); - - } finally { - GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0); - GLStateManager.glDeleteTextures(texId); - } - } - /** * Test glTexImage2D: SharedDrawable uploads texture data, verify dimensions cached correctly. */ @@ -478,7 +464,7 @@ public class GLSM_ServerSideState_UnitTest { GL11.glDeleteTextures(deferredGen); assertTrue(GL11.glIsTexture(texId), "Delete should stay deferred while the splash is not complete"); - setSplashComplete(true); + SplashWindow.setSplashComplete(true); flushGen = GLStateManager.glGenTextures(); assertFalse(GL11.glIsTexture(texId), "Texture should be gone after the post-splash flush"); assertEquals(0, GLStateManager.getBoundTextureForServerState(), "Flush should scrub the main thread binding"); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_SplashCachingCoverage_UnitTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_SplashCachingCoverage_UnitTest.java index 26ffef16f..fb40e9db8 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_SplashCachingCoverage_UnitTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_SplashCachingCoverage_UnitTest.java @@ -5,12 +5,17 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import com.gtnewhorizons.angelica.glsm.backend.BackendManager; import com.gtnewhorizons.angelica.glsm.stacks.BooleanStateStack; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL11; import java.lang.reflect.Field; import java.util.Collections; import java.util.IdentityHashMap; import java.util.Set; +import java.util.function.IntSupplier; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -30,13 +35,23 @@ public class GLSM_SplashCachingCoverage_UnitTest { void leaveSplashWindow() throws IllegalAccessException { SplashWindow.leave(); GLStateManager.glDepthFunc(GL11.GL_LESS); + GLStateManager.glCullFace(GL11.GL_BACK); } - @Test - void depthFuncSetDuringSplashLandsInCache() { - GLStateManager.glDepthFunc(GL11.GL_LEQUAL); + static Stream stateSetDuringSplash() { + return Stream.of( + Arguments.of("depth func", (Runnable) () -> GLStateManager.glDepthFunc(GL11.GL_LEQUAL), GL11.GL_LEQUAL, + (IntSupplier) () -> GLStateManager.getDepthState().getFunc()), + Arguments.of("cull face", (Runnable) () -> GLStateManager.glCullFace(GL11.GL_FRONT), GL11.GL_FRONT, + (IntSupplier) () -> GLStateManager.getPolygonState().getCullFaceMode())); + } - assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "cache write dropped while caching=false"); + @ParameterizedTest(name = "{0}") + @MethodSource("stateSetDuringSplash") + void stateSetDuringSplashLandsInCache(String name, Runnable set, int expected, IntSupplier cached) { + set.run(); + + assertEquals(expected, cached.getAsInt(), "cache write dropped while caching=false"); } @Test @@ -137,11 +152,4 @@ public class GLSM_SplashCachingCoverage_UnitTest { assertFalse(GLStateManager.takeStateSeedPending(), "seed must be consumed exactly once, not once per frame"); assertFalse(GLStateManager.takeStateSeedPending(), "seed must stay consumed"); } - - @Test - void cullFaceSetDuringSplashLandsInCache() { - GLStateManager.glCullFace(GL11.GL_FRONT); - - assertEquals(GL11.GL_FRONT, GLStateManager.getPolygonState().getCullFaceMode(), "cache write dropped while caching=false"); - } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_SplashCompleteLatch_UnitTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_SplashCompleteLatch_UnitTest.java index 36b375155..9c361d029 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_SplashCompleteLatch_UnitTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_SplashCompleteLatch_UnitTest.java @@ -54,18 +54,14 @@ public class GLSM_SplashCompleteLatch_UnitTest { @Test void cachingIsThreadScopedBeforeTheLatchAndGlobalAfter() throws Exception { final AtomicBoolean cachingOffThread = new AtomicBoolean(true); - final Runnable probe = () -> cachingOffThread.set(GLStateManager.isCachingEnabled()); + final TestThreads.Body probe = () -> cachingOffThread.set(GLStateManager.isCachingEnabled()); - Thread t = new Thread(probe, "non-holder"); - t.start(); - t.join(); + TestThreads.run("non-holder", probe); assertFalse(cachingOffThread.get(), "caching must be holder-scoped during the dual-context window"); GLStateManager.markSplashComplete("test"); - t = new Thread(probe, "non-holder"); - t.start(); - t.join(); + TestThreads.run("non-holder", probe); assertTrue(cachingOffThread.get(), "after the latch caching is global - this is what the latch buys"); } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_StateSet_GLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_StateSet_GLTest.java index f6191fb28..f4c35e418 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_StateSet_GLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_StateSet_GLTest.java @@ -1,6 +1,7 @@ package com.gtnewhorizons.angelica.glsm; import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; @@ -11,6 +12,7 @@ import org.lwjgl.opengl.GL20; import java.nio.ByteBuffer; import java.nio.FloatBuffer; +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.driverTextureBinding; import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyIsEnabled; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -20,12 +22,31 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @GLCoreTest public class GLSM_StateSet_GLTest { - private static int driverTextureBinding(int unit) { - final int saved = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE); - GL13.glActiveTexture(GL13.GL_TEXTURE0 + unit); - final int binding = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D); - GL13.glActiveTexture(saved); - return binding; + @AfterEach + void resetState() { + GLStateManager.disableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); + GLStateManager.glBlendEquation(GL14.GL_FUNC_ADD); + GLStateManager.disableLighting(); + GLStateManager.glDisable(GL11.GL_STENCIL_TEST); + GLStateManager.disableDepthTest(); + GLStateManager.glDepthFunc(GL11.GL_LESS); + GLStateManager.glDepthMask(true); + GLStateManager.glColorMask(true, true, true, true); + GLStateManager.glShadeModel(GL11.GL_SMOOTH); + GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL); + GLStateManager.glPolygonOffset(0.0f, 0.0f); + GLStateManager.disableCull(); + GLStateManager.glCullFace(GL11.GL_BACK); + GLStateManager.disableAlphaTest(); + GLStateManager.glAlphaFunc(GL11.GL_ALWAYS, 0.0f); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.disableTexture(); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE); + GLStateManager.disableColorMaterial(); + GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE); } private static boolean[] driverColorWriteMask() { @@ -34,103 +55,63 @@ public class GLSM_StateSet_GLTest { return new boolean[] { buf.get(0) != 0, buf.get(1) != 0, buf.get(2) != 0, buf.get(3) != 0 }; } - private static int compileShader(int type, String src) { - final int s = GL20.glCreateShader(type); - GL20.glShaderSource(s, src); - GL20.glCompileShader(s); - assertEquals(GL11.GL_TRUE, GL20.glGetShaderi(s, GL20.GL_COMPILE_STATUS), () -> "shader compile failed: " + GL20.glGetShaderInfoLog(s, 4096)); - return s; - } - - private static final String TRIVIAL_VS = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }\n"; - private static final String TRIVIAL_FS = "#version 330 core\nout vec4 o;\nvoid main(){ o = vec4(1.0); }\n"; - - static int linkTrivialProgram() { - final int v = compileShader(GL20.GL_VERTEX_SHADER, TRIVIAL_VS); - final int f = compileShader(GL20.GL_FRAGMENT_SHADER, TRIVIAL_FS); - final int p = GL20.glCreateProgram(); - GL20.glAttachShader(p, v); - GL20.glAttachShader(p, f); - GL20.glLinkProgram(p); - assertEquals(GL11.GL_TRUE, GL20.glGetProgrami(p, GL20.GL_LINK_STATUS), () -> "link failed: " + GL20.glGetProgramInfoLog(p, 4096)); - GL20.glDeleteShader(v); - GL20.glDeleteShader(f); - return p; - } - @Test void batchRestoresBlendAxisAndLightingBooleanButNotStencil() { - try { - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.disableLighting(); - GLStateManager.glDisable(GL11.GL_STENCIL_TEST); + GLStateManager.enableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.disableLighting(); + GLStateManager.glDisable(GL11.GL_STENCIL_TEST); - final int d = GLStateManager.pushState(StateSet.BATCH); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); - GLStateManager.enableLighting(); - GLStateManager.glEnable(GL11.GL_STENCIL_TEST); - GLStateManager.popStateTo(d); + final int d = GLStateManager.pushState(StateSet.BATCH); + GLStateManager.disableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); + GLStateManager.enableLighting(); + GLStateManager.glEnable(GL11.GL_STENCIL_TEST); + GLStateManager.popStateTo(d); - verifyIsEnabled(GL11.GL_BLEND, true, "Blend enable - restored by BATCH pop"); - assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb"); - assertEquals(GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_DST_RGB), "driver blend dst rgb"); - assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb"); - assertEquals(GL11.GL_ONE_MINUS_SRC_ALPHA, GLStateManager.getBlendState().getDstRgb(), "cache blend dst rgb"); - verifyIsEnabled(GL11.GL_LIGHTING, false, "Lighting enable - restored by BATCH pop"); - verifyIsEnabled(GL11.GL_STENCIL_TEST, true, "Stencil test - not a BATCH member, must stay toggled"); - } finally { - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.disableLighting(); - GLStateManager.glDisable(GL11.GL_STENCIL_TEST); - } + verifyIsEnabled(GL11.GL_BLEND, true, "Blend enable - restored by BATCH pop"); + assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb"); + assertEquals(GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_DST_RGB), "driver blend dst rgb"); + assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb"); + assertEquals(GL11.GL_ONE_MINUS_SRC_ALPHA, GLStateManager.getBlendState().getDstRgb(), "cache blend dst rgb"); + verifyIsEnabled(GL11.GL_LIGHTING, false, "Lighting enable - restored by BATCH pop"); + verifyIsEnabled(GL11.GL_STENCIL_TEST, true, "Stencil test - not a BATCH member, must stay toggled"); } @Test void fontRestoresDepthAxisShadeModelAndPolygonOffsetButNotStencil() { - try { - GLStateManager.enableDepthTest(); - GLStateManager.glDepthFunc(GL11.GL_LEQUAL); - GLStateManager.glShadeModel(GL11.GL_SMOOTH); - GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL); - GLStateManager.glPolygonOffset(0.0f, 0.0f); - GLStateManager.glEnable(GL11.GL_STENCIL_TEST); + GLStateManager.enableDepthTest(); + GLStateManager.glDepthFunc(GL11.GL_LEQUAL); + GLStateManager.glShadeModel(GL11.GL_SMOOTH); + GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL); + GLStateManager.glPolygonOffset(0.0f, 0.0f); + GLStateManager.glEnable(GL11.GL_STENCIL_TEST); - final int d = GLStateManager.pushState(StateSet.FONT); - GLStateManager.disableDepthTest(); - GLStateManager.glDepthFunc(GL11.GL_ALWAYS); - GLStateManager.glShadeModel(GL11.GL_FLAT); - GLStateManager.glEnable(GL11.GL_POLYGON_OFFSET_FILL); - GLStateManager.glPolygonOffset(-10.0f, -10.0f); - GLStateManager.glDisable(GL11.GL_STENCIL_TEST); - GLStateManager.popStateTo(d); + final int d = GLStateManager.pushState(StateSet.FONT); + GLStateManager.disableDepthTest(); + GLStateManager.glDepthFunc(GL11.GL_ALWAYS); + GLStateManager.glShadeModel(GL11.GL_FLAT); + GLStateManager.glEnable(GL11.GL_POLYGON_OFFSET_FILL); + GLStateManager.glPolygonOffset(-10.0f, -10.0f); + GLStateManager.glDisable(GL11.GL_STENCIL_TEST); + GLStateManager.popStateTo(d); - verifyIsEnabled(GL11.GL_DEPTH_TEST, true, "Depth test enable - restored by FONT pop"); - assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "cache depth func"); - assertEquals(GL11.GL_LEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC), "driver depth func"); - assertEquals(GL11.GL_SMOOTH, GLStateManager.getShadeModelState().getValue(), "cache shade model"); - verifyIsEnabled(GL11.GL_POLYGON_OFFSET_FILL, false, "Polygon offset fill - restored by FONT pop"); - assertEquals(0.0f, GLStateManager.getPolygonState().getOffsetFactor(), "cache polygon offset factor"); - assertEquals(0.0f, GL11.glGetFloat(GL11.GL_POLYGON_OFFSET_FACTOR), "driver polygon offset factor"); - assertEquals(0.0f, GLStateManager.getPolygonState().getOffsetUnits(), "cache polygon offset units"); - assertEquals(0.0f, GL11.glGetFloat(GL11.GL_POLYGON_OFFSET_UNITS), "driver polygon offset units"); - verifyIsEnabled(GL11.GL_STENCIL_TEST, false, "Stencil test - not a FONT member, must stay toggled"); - } finally { - GLStateManager.disableDepthTest(); - GLStateManager.glDepthFunc(GL11.GL_LESS); - GLStateManager.glShadeModel(GL11.GL_SMOOTH); - GLStateManager.glDisable(GL11.GL_POLYGON_OFFSET_FILL); - GLStateManager.glPolygonOffset(0.0f, 0.0f); - GLStateManager.glDisable(GL11.GL_STENCIL_TEST); - } + verifyIsEnabled(GL11.GL_DEPTH_TEST, true, "Depth test enable - restored by FONT pop"); + assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "cache depth func"); + assertEquals(GL11.GL_LEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC), "driver depth func"); + assertEquals(GL11.GL_SMOOTH, GLStateManager.getShadeModelState().getValue(), "cache shade model"); + verifyIsEnabled(GL11.GL_POLYGON_OFFSET_FILL, false, "Polygon offset fill - restored by FONT pop"); + assertEquals(0.0f, GLStateManager.getPolygonState().getOffsetFactor(), "cache polygon offset factor"); + assertEquals(0.0f, GL11.glGetFloat(GL11.GL_POLYGON_OFFSET_FACTOR), "driver polygon offset factor"); + assertEquals(0.0f, GLStateManager.getPolygonState().getOffsetUnits(), "cache polygon offset units"); + assertEquals(0.0f, GL11.glGetFloat(GL11.GL_POLYGON_OFFSET_UNITS), "driver polygon offset units"); + verifyIsEnabled(GL11.GL_STENCIL_TEST, false, "Stencil test - not a FONT member, must stay toggled"); } @Test void fontPipelineRestoresBlendAndDepthFuncButNotProgram() { - final int p0 = linkTrivialProgram(); - final int p1 = linkTrivialProgram(); + final int p0 = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS); + final int p1 = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS); try { GLStateManager.glUseProgram(p0); GLStateManager.enableBlend(); @@ -154,367 +135,270 @@ public class GLSM_StateSet_GLTest { GLStateManager.glUseProgram(0); GLStateManager.glDeleteProgram(p0); GLStateManager.glDeleteProgram(p1); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.glDepthFunc(GL11.GL_LESS); } } @Test void handRestoresBlendAxisAndUnit1TexturingButNotColorMask() { - try { - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.glColorMask(true, true, true, true); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); - GLStateManager.enableTexture(); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.enableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.glColorMask(true, true, true, true); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.enableTexture(); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - final int d = GLStateManager.pushState(StateSet.HAND); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); - GLStateManager.glColorMask(false, false, false, false); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); - GLStateManager.disableTexture(); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - GLStateManager.popStateTo(d); + final int d = GLStateManager.pushState(StateSet.HAND); + GLStateManager.disableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); + GLStateManager.glColorMask(false, false, false, false); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.disableTexture(); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.popStateTo(d); - verifyIsEnabled(GL11.GL_BLEND, true, "Blend enable - restored by HAND pop"); - assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb"); - assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb"); - assertTrue(GLStateManager.getTextures().getTextureUnitStates(1).isEnabled(), "unit 1 texturing - restored by HAND pop"); + verifyIsEnabled(GL11.GL_BLEND, true, "Blend enable - restored by HAND pop"); + assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb"); + assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb"); + assertTrue(GLStateManager.getTextures().getTextureUnitStates(1).isEnabled(), "unit 1 texturing - restored by HAND pop"); - final boolean[] driverMask = driverColorWriteMask(); - assertFalse(driverMask[0] || driverMask[1] || driverMask[2] || driverMask[3], "driver color mask - not a HAND member, must stay toggled"); - assertFalse(GLStateManager.getColorMask().red || GLStateManager.getColorMask().green - || GLStateManager.getColorMask().blue || GLStateManager.getColorMask().alpha, "cache color mask - not a HAND member, must stay toggled"); - } finally { - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.glColorMask(true, true, true, true); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); - GLStateManager.disableTexture(); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - } + final boolean[] driverMask = driverColorWriteMask(); + assertFalse(driverMask[0] || driverMask[1] || driverMask[2] || driverMask[3], "driver color mask - not a HAND member, must stay toggled"); + assertFalse(GLStateManager.getColorMask().red || GLStateManager.getColorMask().green + || GLStateManager.getColorMask().blue || GLStateManager.getColorMask().alpha, "cache color mask - not a HAND member, must stay toggled"); } @Test void masksRestoresDepthMaskAndColorMaskButNotDepthTestEnable() { - try { - GLStateManager.glDepthMask(true); - GLStateManager.glDepthFunc(GL11.GL_LEQUAL); - GLStateManager.glColorMask(true, false, true, false); - GLStateManager.disableDepthTest(); + GLStateManager.glDepthMask(true); + GLStateManager.glDepthFunc(GL11.GL_LEQUAL); + GLStateManager.glColorMask(true, false, true, false); + GLStateManager.disableDepthTest(); - final int d = GLStateManager.pushState(StateSet.MASKS); - GLStateManager.glDepthMask(false); - GLStateManager.glDepthFunc(GL11.GL_ALWAYS); - GLStateManager.glColorMask(false, true, false, true); - GLStateManager.enableDepthTest(); - GLStateManager.popStateTo(d); + final int d = GLStateManager.pushState(StateSet.MASKS); + GLStateManager.glDepthMask(false); + GLStateManager.glDepthFunc(GL11.GL_ALWAYS); + GLStateManager.glColorMask(false, true, false, true); + GLStateManager.enableDepthTest(); + GLStateManager.popStateTo(d); - assertTrue(GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK), "driver depth write mask - restored by MASKS pop"); - assertTrue(GLStateManager.getDepthState().isEnabled(), "cache depth write mask - restored by MASKS pop"); - assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "cache depth func"); - assertEquals(GL11.GL_LEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC), "driver depth func"); + assertTrue(GL11.glGetBoolean(GL11.GL_DEPTH_WRITEMASK), "driver depth write mask - restored by MASKS pop"); + assertTrue(GLStateManager.getDepthState().isEnabled(), "cache depth write mask - restored by MASKS pop"); + assertEquals(GL11.GL_LEQUAL, GLStateManager.getDepthState().getFunc(), "cache depth func"); + assertEquals(GL11.GL_LEQUAL, GL11.glGetInteger(GL11.GL_DEPTH_FUNC), "driver depth func"); - final boolean[] driverMask = driverColorWriteMask(); - assertTrue(driverMask[0] && !driverMask[1] && driverMask[2] && !driverMask[3], "driver color mask - restored by MASKS pop"); - assertTrue(GLStateManager.getColorMask().red && !GLStateManager.getColorMask().green - && GLStateManager.getColorMask().blue && !GLStateManager.getColorMask().alpha, "cache color mask - restored by MASKS pop"); + final boolean[] driverMask = driverColorWriteMask(); + assertTrue(driverMask[0] && !driverMask[1] && driverMask[2] && !driverMask[3], "driver color mask - restored by MASKS pop"); + assertTrue(GLStateManager.getColorMask().red && !GLStateManager.getColorMask().green + && GLStateManager.getColorMask().blue && !GLStateManager.getColorMask().alpha, "cache color mask - restored by MASKS pop"); - verifyIsEnabled(GL11.GL_DEPTH_TEST, true, "Depth test enable - not a MASKS member, must stay toggled"); - } finally { - GLStateManager.glDepthMask(true); - GLStateManager.glDepthFunc(GL11.GL_LESS); - GLStateManager.glColorMask(true, true, true, true); - GLStateManager.disableDepthTest(); - } + verifyIsEnabled(GL11.GL_DEPTH_TEST, true, "Depth test enable - not a MASKS member, must stay toggled"); } @Test void cullRestoresPolygonAxisAndCullEnableButNotBlendFunc() { - try { - GLStateManager.enableCull(); - GLStateManager.glCullFace(GL11.GL_BACK); - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.enableCull(); + GLStateManager.glCullFace(GL11.GL_BACK); + GLStateManager.enableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - final int d = GLStateManager.pushState(StateSet.CULL); - GLStateManager.disableCull(); - GLStateManager.glCullFace(GL11.GL_FRONT); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); - GLStateManager.popStateTo(d); + final int d = GLStateManager.pushState(StateSet.CULL); + GLStateManager.disableCull(); + GLStateManager.glCullFace(GL11.GL_FRONT); + GLStateManager.disableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); + GLStateManager.popStateTo(d); - verifyIsEnabled(GL11.GL_CULL_FACE, true, "Cull enable - restored by CULL pop"); - assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode"); - assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode"); + verifyIsEnabled(GL11.GL_CULL_FACE, true, "Cull enable - restored by CULL pop"); + assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode"); + assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode"); - verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - not a CULL member, must stay toggled"); - assertEquals(GL11.GL_DST_COLOR, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - not a CULL member, must stay toggled"); - assertEquals(GL11.GL_DST_COLOR, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb - not a CULL member, must stay toggled"); - } finally { - GLStateManager.disableCull(); - GLStateManager.glCullFace(GL11.GL_BACK); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - } + verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - not a CULL member, must stay toggled"); + assertEquals(GL11.GL_DST_COLOR, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - not a CULL member, must stay toggled"); + assertEquals(GL11.GL_DST_COLOR, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb - not a CULL member, must stay toggled"); } @Test void blendRestoresBlendAxisButNotCull() { - try { - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.enableCull(); - GLStateManager.glCullFace(GL11.GL_BACK); + GLStateManager.enableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.enableCull(); + GLStateManager.glCullFace(GL11.GL_BACK); - final int d = GLStateManager.pushState(StateSet.BLEND); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); - GLStateManager.disableCull(); - GLStateManager.glCullFace(GL11.GL_FRONT); - GLStateManager.popStateTo(d); + final int d = GLStateManager.pushState(StateSet.BLEND); + GLStateManager.disableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); + GLStateManager.disableCull(); + GLStateManager.glCullFace(GL11.GL_FRONT); + GLStateManager.popStateTo(d); - verifyIsEnabled(GL11.GL_BLEND, true, "Blend enable - restored by BLEND pop"); - assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb"); - assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb"); + verifyIsEnabled(GL11.GL_BLEND, true, "Blend enable - restored by BLEND pop"); + assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb"); + assertEquals(GL11.GL_SRC_ALPHA, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb"); - verifyIsEnabled(GL11.GL_CULL_FACE, false, "Cull enable - not a BLEND member, must stay toggled"); - assertEquals(GL11.GL_FRONT, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - not a BLEND member, must stay toggled"); - assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - not a BLEND member, must stay toggled"); - } finally { - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.disableCull(); - GLStateManager.glCullFace(GL11.GL_BACK); - } + verifyIsEnabled(GL11.GL_CULL_FACE, false, "Cull enable - not a BLEND member, must stay toggled"); + assertEquals(GL11.GL_FRONT, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - not a BLEND member, must stay toggled"); + assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - not a BLEND member, must stay toggled"); } @Test void alphaRestoresAlphaAxisButNotBlend() { - try { - GLStateManager.enableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.enableAlphaTest(); + GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); + GLStateManager.enableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - final int d = GLStateManager.pushState(StateSet.ALPHA); - GLStateManager.disableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_LESS, 0.1f); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); - GLStateManager.popStateTo(d); + final int d = GLStateManager.pushState(StateSet.ALPHA); + GLStateManager.disableAlphaTest(); + GLStateManager.glAlphaFunc(GL11.GL_LESS, 0.1f); + GLStateManager.disableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); + GLStateManager.popStateTo(d); - verifyIsEnabled(GL11.GL_ALPHA_TEST, true, "Alpha test enable - restored by ALPHA pop"); - assertEquals(GL11.GL_GREATER, GLStateManager.getAlphaState().getFunction(), "cache alpha func"); - assertEquals(0.5f, GLStateManager.getAlphaState().getReference(), 0.0001f, "cache alpha ref"); + verifyIsEnabled(GL11.GL_ALPHA_TEST, true, "Alpha test enable - restored by ALPHA pop"); + assertEquals(GL11.GL_GREATER, GLStateManager.getAlphaState().getFunction(), "cache alpha func"); + assertEquals(0.5f, GLStateManager.getAlphaState().getReference(), 0.0001f, "cache alpha ref"); - verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - not an ALPHA member, must stay toggled"); - assertEquals(GL11.GL_DST_COLOR, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - not an ALPHA member, must stay toggled"); - assertEquals(GL11.GL_DST_COLOR, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb - not an ALPHA member, must stay toggled"); - } finally { - GLStateManager.disableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_ALWAYS, 0.0f); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - } + verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - not an ALPHA member, must stay toggled"); + assertEquals(GL11.GL_DST_COLOR, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - not an ALPHA member, must stay toggled"); + assertEquals(GL11.GL_DST_COLOR, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb - not an ALPHA member, must stay toggled"); } @Test void cutoutRestoresAlphaAxisAndUnit1TexturingButNotBlend() { - try { - GLStateManager.enableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); - GLStateManager.enableTexture(); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.enableAlphaTest(); + GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.enableTexture(); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.enableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - final int d = GLStateManager.pushState(StateSet.CUTOUT); - GLStateManager.disableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_LESS, 0.1f); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); - GLStateManager.disableTexture(); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); + final int d = GLStateManager.pushState(StateSet.CUTOUT); + GLStateManager.disableAlphaTest(); + GLStateManager.glAlphaFunc(GL11.GL_LESS, 0.1f); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.disableTexture(); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.disableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); - final int fragBefore = GLStateManager.ctx().fragmentGeneration; - GLStateManager.popStateTo(d); + final int fragBefore = GLStateManager.ctx().fragmentGeneration; + GLStateManager.popStateTo(d); - assertEquals(fragBefore + 1, GLStateManager.ctx().fragmentGeneration, "CUTOUT pop must bump fragment generation once when the value changed inside the bracket"); + assertEquals(fragBefore + 1, GLStateManager.ctx().fragmentGeneration, "CUTOUT pop must bump fragment generation once when the value changed inside the bracket"); - verifyIsEnabled(GL11.GL_ALPHA_TEST, true, "Alpha test enable - restored by CUTOUT pop"); - assertEquals(GL11.GL_GREATER, GLStateManager.getAlphaState().getFunction(), "cache alpha func"); - assertEquals(0.5f, GLStateManager.getAlphaState().getReference(), 0.0001f, "cache alpha ref"); - assertTrue(GLStateManager.getTextures().getTextureUnitStates(1).isEnabled(), "unit 1 texturing - restored by CUTOUT pop"); + verifyIsEnabled(GL11.GL_ALPHA_TEST, true, "Alpha test enable - restored by CUTOUT pop"); + assertEquals(GL11.GL_GREATER, GLStateManager.getAlphaState().getFunction(), "cache alpha func"); + assertEquals(0.5f, GLStateManager.getAlphaState().getReference(), 0.0001f, "cache alpha ref"); + assertTrue(GLStateManager.getTextures().getTextureUnitStates(1).isEnabled(), "unit 1 texturing - restored by CUTOUT pop"); - verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - not a CUTOUT member, must stay toggled"); - assertEquals(GL11.GL_DST_COLOR, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - not a CUTOUT member, must stay toggled"); - assertEquals(GL11.GL_DST_COLOR, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb - not a CUTOUT member, must stay toggled"); - } finally { - GLStateManager.disableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_ALWAYS, 0.0f); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); - GLStateManager.disableTexture(); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - } + verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - not a CUTOUT member, must stay toggled"); + assertEquals(GL11.GL_DST_COLOR, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - not a CUTOUT member, must stay toggled"); + assertEquals(GL11.GL_DST_COLOR, GL11.glGetInteger(GL14.GL_BLEND_SRC_RGB), "driver blend src rgb - not a CUTOUT member, must stay toggled"); } @Test void nonMemberMutationsInsideBracketSaveAndDiscardNothing() { + GLStateManager.enableAlphaTest(); + GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); + GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.glDepthFunc(GL11.GL_LEQUAL); + + final int d = GLStateManager.pushState(StateSet.CUTOUT); + final long discardsBefore = GLStateManager.attribDiscards; try { - GLStateManager.enableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.glDepthFunc(GL11.GL_LEQUAL); - - final int d = GLStateManager.pushState(StateSet.CUTOUT); - final long discardsBefore = GLStateManager.attribDiscards; - try { - final long savedBefore = GLStateManager.attribSlotsSaved; - GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); - GLStateManager.glDepthFunc(GL11.GL_ALWAYS); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE2); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - assertEquals(savedBefore, GLStateManager.attribSlotsSaved, "non-member mutations must not save slots"); - } finally { - GLStateManager.popStateTo(d); - } - - assertEquals(discardsBefore, GLStateManager.attribDiscards, "non-member mutations must not leave slots to discard"); - assertEquals(GL11.GL_DST_COLOR, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - not a CUTOUT member, must stay toggled"); - } finally { - GLStateManager.disableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_ALWAYS, 0.0f); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.glDepthFunc(GL11.GL_LESS); + final long savedBefore = GLStateManager.attribSlotsSaved; + GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); + GLStateManager.glDepthFunc(GL11.GL_ALWAYS); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE2); GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + assertEquals(savedBefore, GLStateManager.attribSlotsSaved, "non-member mutations must not save slots"); + } finally { + GLStateManager.popStateTo(d); } + + assertEquals(discardsBefore, GLStateManager.attribDiscards, "non-member mutations must not leave slots to discard"); + assertEquals(GL11.GL_DST_COLOR, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - not a CUTOUT member, must stay toggled"); } @Test void unchangedSettersSaveNothing() { + GLStateManager.enableAlphaTest(); + GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); + GLStateManager.glDepthFunc(GL11.GL_LEQUAL); + GLStateManager.glDepthMask(true); + GLStateManager.glColorMask(true, true, true, true); + GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.glBlendColor(0.1f, 0.2f, 0.3f, 0.4f); + GLStateManager.glBlendEquation(GL14.GL_FUNC_ADD); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.enableTexture(); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + + final int d = GLStateManager.pushState(StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS)); try { - GLStateManager.enableAlphaTest(); + final long savedBefore = GLStateManager.attribSlotsSaved; GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); GLStateManager.glDepthFunc(GL11.GL_LEQUAL); GLStateManager.glDepthMask(true); GLStateManager.glColorMask(true, true, true, true); - GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); GLStateManager.glBlendColor(0.1f, 0.2f, 0.3f, 0.4f); GLStateManager.glBlendEquation(GL14.GL_FUNC_ADD); + GLStateManager.glBlendEquationSeparate(GL14.GL_FUNC_ADD, GL14.GL_FUNC_ADD); GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); GLStateManager.enableTexture(); GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - - final int d = GLStateManager.pushState(StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS)); - try { - final long savedBefore = GLStateManager.attribSlotsSaved; - GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); - GLStateManager.glDepthFunc(GL11.GL_LEQUAL); - GLStateManager.glDepthMask(true); - GLStateManager.glColorMask(true, true, true, true); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.glBlendColor(0.1f, 0.2f, 0.3f, 0.4f); - GLStateManager.glBlendEquation(GL14.GL_FUNC_ADD); - GLStateManager.glBlendEquationSeparate(GL14.GL_FUNC_ADD, GL14.GL_FUNC_ADD); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); - GLStateManager.enableTexture(); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - assertEquals(1, GLStateManager.attribSlotsSaved - savedBefore, "only the active unit change may save a slot"); - } finally { - GLStateManager.popStateTo(d); - } - - GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - final int d2 = GLStateManager.pushState(StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS)); - try { - final long savedBefore = GLStateManager.attribSlotsSaved; - GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - assertEquals(0, GLStateManager.attribSlotsSaved - savedBefore, "unchanged glBlendFunc must not save a slot"); - } finally { - GLStateManager.popStateTo(d2); - } + assertEquals(1, GLStateManager.attribSlotsSaved - savedBefore, "only the active unit change may save a slot"); } finally { - GLStateManager.disableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_ALWAYS, 0.0f); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.glDepthFunc(GL11.GL_LESS); - GLStateManager.glDepthMask(true); - GLStateManager.glColorMask(true, true, true, true); - GLStateManager.glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); - GLStateManager.glBlendEquation(GL14.GL_FUNC_ADD); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); - GLStateManager.disableTexture(); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.popStateTo(d); + } + + GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + final int d2 = GLStateManager.pushState(StateSet.forMask(GL11.GL_ALL_ATTRIB_BITS)); + try { + final long savedBefore = GLStateManager.attribSlotsSaved; + GLStateManager.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + assertEquals(0, GLStateManager.attribSlotsSaved - savedBefore, "unchanged glBlendFunc must not save a slot"); + } finally { + GLStateManager.popStateTo(d2); } } @Test void bracketDoesNotInheritMembersOfAnEarlierBracketAtItsDepth() { - try { - for (boolean nested : new boolean[] {false, true}) { - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.glDepthFunc(GL11.GL_LESS); - final int outer = nested ? GLStateManager.pushState(StateSet.CULL) : GLStateManager.getAttribDepth(); - try { - GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); - GLStateManager.glPopAttrib(); - final int d = GLStateManager.pushState(StateSet.CUTOUT); - try { - final long savedBefore = GLStateManager.attribSlotsSaved; - GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); - GLStateManager.glDepthFunc(GL11.GL_ALWAYS); - assertEquals(savedBefore, GLStateManager.attribSlotsSaved, "nested=" + nested); - } finally { - GLStateManager.popStateTo(d); - } - } finally { - GLStateManager.popStateTo(outer); - } - } - } finally { + for (boolean nested : new boolean[] {false, true}) { GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); GLStateManager.glDepthFunc(GL11.GL_LESS); - } - } - - @Test - void cullFaceModeRestoredThroughCullBracket() { - try { - GLStateManager.glCullFace(GL11.GL_BACK); - final int d = GLStateManager.pushState(StateSet.CULL); - GLStateManager.glCullFace(GL11.GL_FRONT); - GLStateManager.popStateTo(d); - - assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode"); - assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode"); - } finally { - GLStateManager.glCullFace(GL11.GL_BACK); + final int outer = nested ? GLStateManager.pushState(StateSet.CULL) : GLStateManager.getAttribDepth(); + try { + GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); + GLStateManager.glPopAttrib(); + final int d = GLStateManager.pushState(StateSet.CUTOUT); + try { + final long savedBefore = GLStateManager.attribSlotsSaved; + GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); + GLStateManager.glDepthFunc(GL11.GL_ALWAYS); + assertEquals(savedBefore, GLStateManager.attribSlotsSaved, "nested=" + nested); + } finally { + GLStateManager.popStateTo(d); + } + } finally { + GLStateManager.popStateTo(outer); + } } } @Test void cullFaceModeRestoredThroughBatchBracketRegression2117() { - try { - GLStateManager.glCullFace(GL11.GL_BACK); - final int d = GLStateManager.pushState(StateSet.BATCH); - GLStateManager.glCullFace(GL11.GL_FRONT); - GLStateManager.popStateTo(d); + GLStateManager.glCullFace(GL11.GL_BACK); + final int d = GLStateManager.pushState(StateSet.BATCH); + GLStateManager.glCullFace(GL11.GL_FRONT); + GLStateManager.popStateTo(d); - assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode"); - assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode"); - } finally { - GLStateManager.glCullFace(GL11.GL_BACK); - } + assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode"); + assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode"); } @Test @@ -572,19 +456,14 @@ public class GLSM_StateSet_GLTest { @Test void popStateToRepairsUnbalancedInnerPush() { - try { - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - final int d = GLStateManager.pushState(StateSet.BLEND); - GLStateManager.pushState(StateSet.CULL); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.popStateTo(d); - assertEquals(d, GLStateManager.getAttribDepth()); - assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb(), "popStateTo repairs the unbalanced non-member push and carries the change through to the BLEND member pop, restoring the pre-bracket baseline"); - } finally { - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - } + GLStateManager.disableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); + final int d = GLStateManager.pushState(StateSet.BLEND); + GLStateManager.pushState(StateSet.CULL); + GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.popStateTo(d); + assertEquals(d, GLStateManager.getAttribDepth()); + assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb(), "popStateTo repairs the unbalanced non-member push and carries the change through to the BLEND member pop, restoring the pre-bracket baseline"); } @Test @@ -598,103 +477,84 @@ public class GLSM_StateSet_GLTest { @Test void exceptionMidBracketLeavesDepthAndStateCorrectViaFinally() { - try { - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - final int depthBefore = GLStateManager.getAttribDepth(); + GLStateManager.enableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + final int depthBefore = GLStateManager.getAttribDepth(); - final RuntimeException thrown = assertThrows(RuntimeException.class, () -> { - final int d = GLStateManager.pushState(StateSet.BLEND); - try { - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); - throw new RuntimeException("boom"); - } finally { - GLStateManager.popStateTo(d); - } - }); + final RuntimeException thrown = assertThrows(RuntimeException.class, () -> { + final int d = GLStateManager.pushState(StateSet.BLEND); + try { + GLStateManager.disableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); + throw new RuntimeException("boom"); + } finally { + GLStateManager.popStateTo(d); + } + }); - assertEquals("boom", thrown.getMessage()); - assertEquals(depthBefore, GLStateManager.getAttribDepth()); - verifyIsEnabled(GL11.GL_BLEND, true, "Blend enable - restored despite exception"); - assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - restored despite exception"); - } finally { - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - } + assertEquals("boom", thrown.getMessage()); + assertEquals(depthBefore, GLStateManager.getAttribDepth()); + verifyIsEnabled(GL11.GL_BLEND, true, "Blend enable - restored despite exception"); + assertEquals(GL11.GL_SRC_ALPHA, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - restored despite exception"); } @Test void allAttribBitsNestedInsideInternalBracketRestoresBothLevels() { - try { - GLStateManager.enableCull(); - GLStateManager.glCullFace(GL11.GL_BACK); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.enableCull(); + GLStateManager.glCullFace(GL11.GL_BACK); + GLStateManager.disableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - final int d = GLStateManager.pushState(StateSet.BLEND); - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + final int d = GLStateManager.pushState(StateSet.BLEND); + GLStateManager.enableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); - GLStateManager.glCullFace(GL11.GL_FRONT); - GLStateManager.glPopAttrib(); + GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); + GLStateManager.glCullFace(GL11.GL_FRONT); + GLStateManager.glPopAttrib(); - verifyIsEnabled(GL11.GL_CULL_FACE, true, "Cull enable - restored by inner GL_ALL_ATTRIB_BITS pop"); - assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - restored by inner GL_ALL_ATTRIB_BITS pop"); - assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - restored by inner GL_ALL_ATTRIB_BITS pop"); + verifyIsEnabled(GL11.GL_CULL_FACE, true, "Cull enable - restored by inner GL_ALL_ATTRIB_BITS pop"); + assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - restored by inner GL_ALL_ATTRIB_BITS pop"); + assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - restored by inner GL_ALL_ATTRIB_BITS pop"); - GLStateManager.popStateTo(d); + GLStateManager.popStateTo(d); - verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - restored by outer BLEND pop"); - assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - restored by outer BLEND pop"); - } finally { - GLStateManager.disableCull(); - GLStateManager.glCullFace(GL11.GL_BACK); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - } + verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - restored by outer BLEND pop"); + assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - restored by outer BLEND pop"); } @Test void internalBracketNestedInsideAllAttribBitsRestoresBothLevels() { + GLStateManager.disableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.enableCull(); + GLStateManager.glCullFace(GL11.GL_BACK); + + GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); try { - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.enableCull(); - GLStateManager.glCullFace(GL11.GL_BACK); + GLStateManager.glCullFace(GL11.GL_FRONT); - GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); - try { - GLStateManager.glCullFace(GL11.GL_FRONT); + final int d = GLStateManager.pushState(StateSet.BLEND); + GLStateManager.enableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.popStateTo(d); - final int d = GLStateManager.pushState(StateSet.BLEND); - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - GLStateManager.popStateTo(d); - - verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - restored by inner BLEND pop"); - assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - restored by inner BLEND pop"); - assertEquals(GL11.GL_FRONT, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - change survives, not a BLEND member"); - assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - change survives, not a BLEND member"); - } finally { - GLStateManager.glPopAttrib(); - } - - assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - restored by outer GL_ALL_ATTRIB_BITS pop"); - assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - restored by outer GL_ALL_ATTRIB_BITS pop"); + verifyIsEnabled(GL11.GL_BLEND, false, "Blend enable - restored by inner BLEND pop"); + assertEquals(GL11.GL_ONE, GLStateManager.getBlendState().getSrcRgb(), "cache blend src rgb - restored by inner BLEND pop"); + assertEquals(GL11.GL_FRONT, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - change survives, not a BLEND member"); + assertEquals(GL11.GL_FRONT, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - change survives, not a BLEND member"); } finally { - GLStateManager.disableCull(); - GLStateManager.glCullFace(GL11.GL_BACK); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.glPopAttrib(); } + + assertEquals(GL11.GL_BACK, GLStateManager.getPolygonState().getCullFaceMode(), "cache cull face mode - restored by outer GL_ALL_ATTRIB_BITS pop"); + assertEquals(GL11.GL_BACK, GL11.glGetInteger(GL11.GL_CULL_FACE_MODE), "driver cull face mode - restored by outer GL_ALL_ATTRIB_BITS pop"); } @Test void rProgramFontRestoresRealProgramToDriverAndCache() { - final int p0 = linkTrivialProgram(); - final int p1 = linkTrivialProgram(); + final int p0 = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS); + final int p1 = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS); try { GLStateManager.glUseProgram(p0); @@ -714,7 +574,7 @@ public class GLSM_StateSet_GLTest { @Test void rProgramFontReactivatesFfpAfterBracket() { - final int p1 = linkTrivialProgram(); + final int p1 = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS); try { GLStateManager.glUseProgram(0); assertTrue(ShaderManager.getInstance().isActive(), "FFP must be active before the bracket"); @@ -734,43 +594,33 @@ public class GLSM_StateSet_GLTest { @Test void blendPopDoesNotBumpFragmentOrLightingGeneration() { - try { - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + GLStateManager.enableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - final int d = GLStateManager.pushState(StateSet.BLEND); - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); + final int d = GLStateManager.pushState(StateSet.BLEND); + GLStateManager.disableBlend(); + GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); - final int fragBefore = GLStateManager.ctx().fragmentGeneration; - final int lightBefore = GLStateManager.ctx().lightingGeneration; - GLStateManager.popStateTo(d); + final int fragBefore = GLStateManager.ctx().fragmentGeneration; + final int lightBefore = GLStateManager.ctx().lightingGeneration; + GLStateManager.popStateTo(d); - assertEquals(fragBefore, GLStateManager.ctx().fragmentGeneration, "BLEND pop must not bump fragment generation"); - assertEquals(lightBefore, GLStateManager.ctx().lightingGeneration, "BLEND pop must not bump lighting generation"); - } finally { - GLStateManager.disableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO); - } + assertEquals(fragBefore, GLStateManager.ctx().fragmentGeneration, "BLEND pop must not bump fragment generation"); + assertEquals(lightBefore, GLStateManager.ctx().lightingGeneration, "BLEND pop must not bump lighting generation"); } @Test void alphaPopBumpsFragmentGenerationWhenChanged() { - try { - GLStateManager.enableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); + GLStateManager.enableAlphaTest(); + GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); - final int d = GLStateManager.pushState(StateSet.ALPHA); - GLStateManager.glAlphaFunc(GL11.GL_LESS, 0.1f); + final int d = GLStateManager.pushState(StateSet.ALPHA); + GLStateManager.glAlphaFunc(GL11.GL_LESS, 0.1f); - final int fragBefore = GLStateManager.ctx().fragmentGeneration; - GLStateManager.popStateTo(d); + final int fragBefore = GLStateManager.ctx().fragmentGeneration; + GLStateManager.popStateTo(d); - assertEquals(fragBefore + 1, GLStateManager.ctx().fragmentGeneration, "ALPHA pop must bump fragment generation once when the value changed inside the bracket"); - } finally { - GLStateManager.disableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_ALWAYS, 0.0f); - } + assertEquals(fragBefore + 1, GLStateManager.ctx().fragmentGeneration, "ALPHA pop must bump fragment generation once when the value changed inside the bracket"); } @Test @@ -804,36 +654,27 @@ public class GLSM_StateSet_GLTest { final FloatBuffer zero = BufferUtils.createFloatBuffer(4); zero.put(0.0f).put(0.0f).put(0.0f).put(0.0f).flip(); GLStateManager.glTexEnv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, zero); - try { - final int d = GLStateManager.pushState(StateSet.forMask(GL11.GL_TEXTURE_BIT)); - GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_DECAL); - final FloatBuffer dirty = BufferUtils.createFloatBuffer(4); - dirty.put(0.1f).put(0.2f).put(0.3f).put(0.4f).flip(); - GLStateManager.glTexEnv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, dirty); - GLStateManager.popStateTo(d); - assertEquals(GL11.GL_MODULATE, GLStateManager.getTextures().getTexEnvState(0).mode, "cache tex env mode restored by GL_TEXTURE_BIT pop"); - assertEquals(0.0f, GLStateManager.getTextures().getTexEnvState(0).envColorR, 0.0001f, "cache tex env color restored by GL_TEXTURE_BIT pop"); - } finally { - GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE); - } + final int d = GLStateManager.pushState(StateSet.forMask(GL11.GL_TEXTURE_BIT)); + GLStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_DECAL); + final FloatBuffer dirty = BufferUtils.createFloatBuffer(4); + dirty.put(0.1f).put(0.2f).put(0.3f).put(0.4f).flip(); + GLStateManager.glTexEnv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, dirty); + GLStateManager.popStateTo(d); + assertEquals(GL11.GL_MODULATE, GLStateManager.getTextures().getTexEnvState(0).mode, "cache tex env mode restored by GL_TEXTURE_BIT pop"); + assertEquals(0.0f, GLStateManager.getTextures().getTexEnvState(0).envColorR, 0.0001f, "cache tex env color restored by GL_TEXTURE_BIT pop"); } @Test void lightingBitBracketRestoresBakedFrontMaterialAfterColorMaterialToggle() { - try { - GLStateManager.glColor4f(0.9f, 0.9f, 0.9f, 0.9f); - GLStateManager.enableColorMaterial(); - GLStateManager.disableColorMaterial(); - final int d = GLStateManager.pushState(StateSet.forMask(GL11.GL_LIGHTING_BIT)); - GLStateManager.glColor4f(0.1f, 0.2f, 0.3f, 0.4f); - GLStateManager.enableColorMaterial(); - GLStateManager.popStateTo(d); - assertEquals(0.9f, GLStateManager.getFrontMaterial().ambient.x, 0.0001f, "cache front material ambient restored by GL_LIGHTING_BIT pop"); - assertEquals(0.9f, GLStateManager.getFrontMaterial().diffuse.x, 0.0001f, "cache front material diffuse restored by GL_LIGHTING_BIT pop"); - } finally { - GLStateManager.disableColorMaterial(); - GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT_AND_DIFFUSE); - } + GLStateManager.glColor4f(0.9f, 0.9f, 0.9f, 0.9f); + GLStateManager.enableColorMaterial(); + GLStateManager.disableColorMaterial(); + final int d = GLStateManager.pushState(StateSet.forMask(GL11.GL_LIGHTING_BIT)); + GLStateManager.glColor4f(0.1f, 0.2f, 0.3f, 0.4f); + GLStateManager.enableColorMaterial(); + GLStateManager.popStateTo(d); + assertEquals(0.9f, GLStateManager.getFrontMaterial().ambient.x, 0.0001f, "cache front material ambient restored by GL_LIGHTING_BIT pop"); + assertEquals(0.9f, GLStateManager.getFrontMaterial().diffuse.x, 0.0001f, "cache front material diffuse restored by GL_LIGHTING_BIT pop"); } @Test diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_TextureBindingTargetCache_UnitTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_TextureBindingTargetCache_UnitTest.java index 356dbbc18..0e842718f 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_TextureBindingTargetCache_UnitTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_TextureBindingTargetCache_UnitTest.java @@ -10,13 +10,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @GLCoreTest public class GLSM_TextureBindingTargetCache_UnitTest { - private static void drainErrors() { - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} - } - @Test void flattenedCacheTracksNon2DTargets() { - drainErrors(); final int t3d = GL11.glGenTextures(); final int t2d = GL11.glGenTextures(); try { @@ -38,13 +33,11 @@ public class GLSM_TextureBindingTargetCache_UnitTest { GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); GLStateManager.glDeleteTextures(t3d); GLStateManager.glDeleteTextures(t2d); - drainErrors(); } } @Test void proxyTargetBindIsNeitherForwardedNorCached() { - drainErrors(); final int t2d = GL11.glGenTextures(); try { GLStateManager.glActiveTexture(GL13.GL_TEXTURE5); @@ -60,13 +53,11 @@ public class GLSM_TextureBindingTargetCache_UnitTest { GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0); GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); GLStateManager.glDeleteTextures(t2d); - drainErrors(); } } @Test void popAttribRestoresRecordedTarget() { - drainErrors(); final int t3d = GL11.glGenTextures(); final int t2d = GL11.glGenTextures(); try { @@ -86,13 +77,11 @@ public class GLSM_TextureBindingTargetCache_UnitTest { GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); GLStateManager.glDeleteTextures(t3d); GLStateManager.glDeleteTextures(t2d); - drainErrors(); } } @Test void unbindIsNotElidedAcrossTargets() { - drainErrors(); final int t2d = GL11.glGenTextures(); try { GLStateManager.glActiveTexture(GL13.GL_TEXTURE4); @@ -100,11 +89,9 @@ public class GLSM_TextureBindingTargetCache_UnitTest { GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, 0); GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, 0); assertEquals(0, GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D), "2D unbind after a 3D unbind must reach the driver"); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } finally { GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); GLStateManager.glDeleteTextures(t2d); - drainErrors(); } } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_TextureUploadGeneration_GLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_TextureUploadGeneration_GLTest.java index c494937bb..785df46f8 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_TextureUploadGeneration_GLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_TextureUploadGeneration_GLTest.java @@ -79,6 +79,8 @@ public class GLSM_TextureUploadGeneration_GLTest { final int afterSub = info.getUploadGeneration(); GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 1, 0, 0, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, BufferUtils.createByteBuffer(4)); assertEquals(afterSub, info.getUploadGeneration(), "only level 0 is tracked"); + // Level 1 was never allocated, so the driver rejects that upload. + assertEquals(GL11.GL_INVALID_OPERATION, GL11.glGetError()); } finally { GLStateManager.glDeleteTextures(texId); } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_VAO_UnitTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_VAO_UnitTest.java index 7b7fc97e4..46f7ad463 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_VAO_UnitTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_VAO_UnitTest.java @@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.glsm; import com.gtnewhorizon.gtnhlib.client.opengl.UniversalVAO; import com.gtnewhorizons.angelica.glsm.ffp.VAOManager; import com.gtnewhorizons.angelica.util.GLSMUtil; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.lwjgl.opengl.GL11; @@ -15,6 +16,13 @@ import static com.gtnewhorizons.angelica.util.GLSMUtil.verifyState; @GLCompatTest public class GLSM_VAO_UnitTest { + @AfterEach + void resetClientArrayState() { + glBindVertexArray(0); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + GLSMUtil.setClientState(GL11.GL_NORMAL_ARRAY, false); + } + @Test void testDefaultStates() { verifyState(GL30.GL_VERTEX_ARRAY_BINDING, GLStateManager.ctx().defaultVAO, "GL_VERTEX_ARRAY_BINDING Initial State"); @@ -36,14 +44,14 @@ public class GLSM_VAO_UnitTest { final int vaoVertexType = GL11.GL_INT; final int vaoVertexStride = vaoVertexSize * 4; - GL13.glClientActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); verifyState(GL13.GL_CLIENT_ACTIVE_TEXTURE, GL13.GL_TEXTURE1, "GL_CLIENT_ACTIVE_TEXTURE - Setup"); // Set the vertex attrib to a defined state int vbo = GL15.glGenBuffers(); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); - GL11.glVertexPointer(defaultVertexSize, defaultVertexType, defaultVertexStride, 0); + GLStateManager.glVertexPointer(defaultVertexSize, defaultVertexType, defaultVertexStride, 0L); GLSMUtil.setClientState(GL11.GL_NORMAL_ARRAY, true); verifyState(GL11.GL_NORMAL_ARRAY, true, "GL_NORMAL_ARRAY - Enabled"); @@ -74,7 +82,7 @@ public class GLSM_VAO_UnitTest { GLSMUtil.setClientState(GL11.GL_VERTEX_ARRAY, vaoVertexEnabled); verifyState(GL11.GL_VERTEX_ARRAY, vaoVertexEnabled, "GL_VERTEX_ARRAY - Enabled"); - GL11.glVertexPointer(vaoVertexSize, vaoVertexType, vaoVertexStride, 0); + GLStateManager.glVertexPointer(vaoVertexSize, vaoVertexType, vaoVertexStride, 0L); verifyState(GL11.GL_VERTEX_ARRAY_SIZE, vaoVertexSize, "GL_VERTEX_ARRAY_SIZE - Set"); verifyState(GL11.GL_VERTEX_ARRAY_TYPE, vaoVertexType, "GL_VERTEX_ARRAY_TYPE - Set"); @@ -117,7 +125,7 @@ public class GLSM_VAO_UnitTest { GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); glBindVertexArray(0); - GL15.glDeleteBuffers(vbo); + GLStateManager.glDeleteBuffers(vbo); UniversalVAO.deleteVertexArrays(vao); } @@ -138,7 +146,7 @@ public class GLSM_VAO_UnitTest { GLStateManager.glDisableVertexAttribArray(0); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); glBindVertexArray(0); - GL15.glDeleteBuffers(vbo); + GLStateManager.glDeleteBuffers(vbo); UniversalVAO.deleteVertexArrays(vao); } @@ -159,7 +167,7 @@ public class GLSM_VAO_UnitTest { GLStateManager.glDisableVertexAttribArray(0); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); glBindVertexArray(0); - GL15.glDeleteBuffers(vbo); + GLStateManager.glDeleteBuffers(vbo); UniversalVAO.deleteVertexArrays(vao); } @@ -179,7 +187,7 @@ public class GLSM_VAO_UnitTest { GLStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); glBindVertexArray(0); - GL15.glDeleteBuffers(vbo); + GLStateManager.glDeleteBuffers(vbo); UniversalVAO.deleteVertexArrays(vao); } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ValueStackCow_GLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ValueStackCow_GLTest.java index 1658b09b5..f257d9b30 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ValueStackCow_GLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/GLSM_ValueStackCow_GLTest.java @@ -31,6 +31,9 @@ public class GLSM_ValueStackCow_GLTest { if (fixtureTexA == -1) { fixtureTexA = GLStateManager.glGenTextures(); fixtureTexB = GLStateManager.glGenTextures(); + final int bound = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D); + GL11.glBindTexture(GL11.GL_TEXTURE_2D, fixtureTexB); + GL11.glBindTexture(GL11.GL_TEXTURE_2D, bound); } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ImmediateModeThreadIsolationTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ImmediateModeThreadIsolationTest.java index 3c7efea70..31b714590 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ImmediateModeThreadIsolationTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ImmediateModeThreadIsolationTest.java @@ -4,12 +4,12 @@ import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator; import com.gtnewhorizons.angelica.glsm.recording.CompiledDisplayList; import com.gtnewhorizons.angelica.glsm.recording.GLCommand; import com.gtnewhorizons.angelica.glsm.recording.ImmediateModeRecorder; +import com.gtnewhorizons.angelica.glsm.testutil.TestThreads; import org.junit.jupiter.api.Test; import org.lwjgl.opengl.GL11; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -29,10 +29,10 @@ class ImmediateModeThreadIsolationTest { final AtomicReference bg1 = new AtomicReference<>(); final AtomicReference bg2 = new AtomicReference<>(); - final Thread t1 = new Thread(() -> bg1.set(ImmediateModeRecorder.getInternalTessellator()), "iso-test-1"); - final Thread t2 = new Thread(() -> bg2.set(ImmediateModeRecorder.getInternalTessellator()), "iso-test-2"); - t1.start(); t2.start(); - t1.join(); t2.join(); + final TestThreads.Worker t1 = TestThreads.start("iso-test-1", () -> bg1.set(ImmediateModeRecorder.getInternalTessellator())); + final TestThreads.Worker t2 = TestThreads.start("iso-test-2", () -> bg2.set(ImmediateModeRecorder.getInternalTessellator())); + t1.join(); + t2.join(); assertNotNull(bg1.get()); assertNotNull(bg2.get()); @@ -43,28 +43,22 @@ class ImmediateModeThreadIsolationTest { @Test void concurrentImmediateModeDoesNotCorruptRecording() throws Exception { - final List failures = new CopyOnWriteArrayList<>(); final CountDownLatch start = new CountDownLatch(1); final AtomicBoolean stop = new AtomicBoolean(false); - final Runnable spam = () -> { - try { - start.await(); - while (!stop.get()) { - GLStateManager.glBegin(GL11.GL_QUADS); - GLStateManager.glVertex3f(0, 0, 0); - GLStateManager.glVertex3f(1, 0, 0); - GLStateManager.glVertex3f(1, 1, 0); - GLStateManager.glVertex3f(0, 1, 0); - GLStateManager.glEnd(); - } - } catch (Throwable t) { - failures.add(t); + final TestThreads.Body spam = () -> { + start.await(); + while (!stop.get()) { + GLStateManager.glBegin(GL11.GL_QUADS); + GLStateManager.glVertex3f(0, 0, 0); + GLStateManager.glVertex3f(1, 0, 0); + GLStateManager.glVertex3f(1, 1, 0); + GLStateManager.glVertex3f(0, 1, 0); + GLStateManager.glEnd(); } }; - final Thread t1 = new Thread(spam, "iso-spam-1"); - final Thread t2 = new Thread(spam, "iso-spam-2"); - t1.start(); t2.start(); + final TestThreads.Worker t1 = TestThreads.start("iso-spam-1", spam); + final TestThreads.Worker t2 = TestThreads.start("iso-spam-2", spam); start.countDown(); final List lists = new ArrayList<>(); @@ -83,10 +77,10 @@ class ImmediateModeThreadIsolationTest { } } finally { stop.set(true); - t1.join(5000); t2.join(5000); } + t1.join(); + t2.join(); - assertTrue(failures.isEmpty(), "background immediate mode threw: " + failures); for (int list : lists) { final CompiledDisplayList compiled = DisplayListManager.getDisplayList(list); assertNotNull(compiled, "list " + list + " should be compiled"); @@ -97,9 +91,7 @@ class ImmediateModeThreadIsolationTest { @Test void sweepFreesDeadThreadTessellators() throws Exception { - final Thread t = new Thread(() -> ImmediateModeRecorder.getInternalTessellator(), "iso-sweep"); - t.start(); - t.join(); + TestThreads.run("iso-sweep", ImmediateModeRecorder::getInternalTessellator); final int before = ImmediateModeRecorder.getThreadTessellatorCount(); assertTrue(before >= 1); ImmediateModeRecorder.cleanupOrphanTessellators(); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/InstancedQuadDrawGLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/InstancedQuadDrawGLTest.java index 52a58147c..9f78c17c8 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/InstancedQuadDrawGLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/InstancedQuadDrawGLTest.java @@ -2,17 +2,19 @@ package com.gtnewhorizons.angelica.glsm; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL20; import java.nio.FloatBuffer; import java.nio.IntBuffer; +import java.util.function.IntConsumer; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; @GLCoreTest class InstancedQuadDrawGLTest { @@ -26,7 +28,7 @@ class InstancedQuadDrawGLTest { @BeforeEach void setup() { - program = buildProgram(); + program = GlTestSupport.program(VS, FS); GLStateManager.glUseProgram(program); final FloatBuffer verts = BufferUtils.createFloatBuffer(8); @@ -42,63 +44,39 @@ class InstancedQuadDrawGLTest { ebo = GLStateManager.glGenBuffers(); GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo); GLStateManager.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, idx, GL15.GL_STATIC_DRAW); - - drainErrors(); } @AfterEach void cleanup() { GLStateManager.glUseProgram(0); - if (program != 0) GL20.glDeleteProgram(program); + if (program != 0) GLStateManager.glDeleteProgram(program); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); if (vbo != 0) GLStateManager.glDeleteBuffers(vbo); if (ebo != 0) GLStateManager.glDeleteBuffers(ebo); - drainErrors(); } - @Test - void drawArraysInstanced_quads_noInvalidEnum() { - GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, 4, 3); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "GL_QUADS instanced array draw must be emulated, not rejected"); + static Stream draws() { + return Stream.of( + draw("GL_QUADS instanced array draw must be emulated, not rejected", ebo -> GLStateManager.glDrawArraysInstanced(GL11.GL_QUADS, 0, 4, 3)), + draw("non-quad instanced array draw passes through cleanly", ebo -> { + GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); + GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 3); + }), + draw("GL_QUADS instanced element draw must be emulated, not rejected", ebo -> { + GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo); + GLStateManager.glDrawElementsInstanced(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_INT, 0L, 3); + })); } - @Test - void drawArraysInstanced_triangles_fastPathClean() { - GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); - GLStateManager.glDrawArraysInstanced(GL11.GL_TRIANGLES, 0, 3, 3); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "non-quad instanced array draw passes through cleanly"); + private static Arguments draw(String name, IntConsumer body) { + return Arguments.of(name, body); } - @Test - void drawElementsInstanced_quads_noInvalidEnum() { - GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo); - GLStateManager.glDrawElementsInstanced(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_INT, 0L, 3); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "GL_QUADS instanced element draw must be emulated, not rejected"); - } - - private static int buildProgram() { - final int vs = compile(GL20.GL_VERTEX_SHADER, VS); - final int fs = compile(GL20.GL_FRAGMENT_SHADER, FS); - final int p = GL20.glCreateProgram(); - GL20.glAttachShader(p, vs); - GL20.glAttachShader(p, fs); - GL20.glLinkProgram(p); - assertNotEquals(0, GL20.glGetProgrami(p, GL20.GL_LINK_STATUS), "test shader must link: " + GL20.glGetProgramInfoLog(p, 1024)); - GL20.glDeleteShader(vs); - GL20.glDeleteShader(fs); - return p; - } - - private static int compile(int type, String src) { - final int s = GL20.glCreateShader(type); - GL20.glShaderSource(s, src); - GL20.glCompileShader(s); - assertNotEquals(0, GL20.glGetShaderi(s, GL20.GL_COMPILE_STATUS), "test shader must compile: " + GL20.glGetShaderInfoLog(s, 1024)); - return s; - } - - private static void drainErrors() { - while (GL11.glGetError() != GL11.GL_NO_ERROR) { /* clear */ } + @ParameterizedTest(name = "{0}") + @MethodSource("draws") + void instancedDrawRaisesNoGlError(String name, IntConsumer draw) { + draw.accept(ebo); + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), name); } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/QuadConverterTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/QuadConverterTest.java index a6c316893..36c87ac19 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/QuadConverterTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/QuadConverterTest.java @@ -81,11 +81,6 @@ class QuadConverterTest { } } - @Test - void triangulateQuads_zeroQuads_noop() { - QuadConverter.triangulateQuads(0L, GL11.GL_UNSIGNED_INT, 0L, GL11.GL_UNSIGNED_INT, 0); - } - @Test void triangulateQuads_unsupportedTypes_throw() { final ByteBuffer src = MemoryUtilities.memAlloc(16); @@ -240,8 +235,6 @@ class QuadConverterTest { } } - // === helpers === - private static void assertUintIndices(ByteBuffer buf, int... expected) { for (int i = 0; i < expected.length; i++) { assertEquals(expected[i], buf.getInt(i * 4), "uint index[" + i + "]"); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ReadbackGLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ReadbackGLTest.java index 8739a2dd9..8f12cf820 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ReadbackGLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ReadbackGLTest.java @@ -1,72 +1,42 @@ package com.gtnewhorizons.angelica.glsm; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.lwjgl.opengl.GL11; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.stream.Stream; @GLCoreTest class ReadbackGLTest { @AfterEach - void noErrors() { + void resetPack() { ReadbackFixture.resetPack(); - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } - @Test - void rgba8() { - ReadbackFixture.rgba8(); + static Stream readbacks() { + return Stream.of( + format("rgba8", ReadbackFixture::rgba8), + format("packState", ReadbackFixture::packState), + format("pixelPackBuffer", ReadbackFixture::pixelPackBuffer), + format("rgb8", ReadbackFixture::rgb8), + format("rgba16f", ReadbackFixture::rgba16f), + format("rgba32f", ReadbackFixture::rgba32f), + format("r8", ReadbackFixture::r8), + format("rg16f", ReadbackFixture::rg16f), + format("rgb10a2", ReadbackFixture::rgb10a2), + format("r11g11b10f", ReadbackFixture::r11g11b10f), + format("depthStencil", ReadbackFixture::depthStencil)); } - @Test - void packState() { - ReadbackFixture.packState(); + private static Arguments format(String name, Runnable body) { + return Arguments.of(name, body); } - @Test - void pixelPackBuffer() { - ReadbackFixture.pixelPackBuffer(); - } - - @Test - void rgb8() { - ReadbackFixture.rgb8(); - } - - @Test - void rgba16f() { - ReadbackFixture.rgba16f(); - } - - @Test - void rgba32f() { - ReadbackFixture.rgba32f(); - } - - @Test - void r8() { - ReadbackFixture.r8(); - } - - @Test - void rg16f() { - ReadbackFixture.rg16f(); - } - - @Test - void rgb10a2() { - ReadbackFixture.rgb10a2(); - } - - @Test - void r11g11b10f() { - ReadbackFixture.r11g11b10f(); - } - - @Test - void depthStencil() { - ReadbackFixture.depthStencil(); + @ParameterizedTest(name = "{0}") + @MethodSource("readbacks") + void readback(String name, Runnable body) { + body.run(); } } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/dsa/DSAUnsupportedBindingTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/dsa/DSAUnsupportedBindingTest.java index c3f4fab8c..3c935ab52 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/dsa/DSAUnsupportedBindingTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/dsa/DSAUnsupportedBindingTest.java @@ -9,6 +9,7 @@ import org.lwjgl.opengl.GL13; import java.nio.ByteBuffer; +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors; import static org.junit.jupiter.api.Assertions.assertEquals; @GLCoreTest @@ -71,8 +72,7 @@ public class DSAUnsupportedBindingTest { GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, t); GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, y); GLStateManager.glBindTexture(GL12.GL_TEXTURE_3D, x); - while (GL11.glGetError() != GL11.GL_NO_ERROR) { - } + drainErrors(); dsa.texParameteri(t, GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/FFPVertexLightingGLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/FFPVertexLightingGLTest.java index 6fd8d3736..f7fb0b55f 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/FFPVertexLightingGLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/FFPVertexLightingGLTest.java @@ -7,15 +7,19 @@ import org.joml.Vector3f; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL15; import java.nio.ByteBuffer; -import java.nio.FloatBuffer; +import java.util.stream.Stream; import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.COLOR_BIT; +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.floats; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -112,12 +116,6 @@ class FFPVertexLightingGLTest { + " light1=" + GLStateManager.getLightStates()[1].isEnabled(); } - private static FloatBuffer floats(float... values) { - final FloatBuffer buf = BufferUtils.createFloatBuffer(values.length); - buf.put(values).flip(); - return buf; - } - private void buildTriangle() { vao = GLStateManager.glGenVertexArrays(); GLStateManager.glBindVertexArray(vao); @@ -156,9 +154,8 @@ class FFPVertexLightingGLTest { GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 3); assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "FFP draw must not raise a GL error"); - final ByteBuffer pixel = BufferUtils.createByteBuffer(4); - GL11.glReadPixels(400, 200, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixel); - return new float[] { (pixel.get(0) & 0xFF) / 255.0f, (pixel.get(1) & 0xFF) / 255.0f, (pixel.get(2) & 0xFF) / 255.0f }; + final int[] pixel = FfpFixture.readPixel(400, 200); + return new float[] { pixel[0] / 255.0f, pixel[1] / 255.0f, pixel[2] / 255.0f }; } private void assertSolverMatchesShader(String context) { @@ -170,23 +167,20 @@ class FFPVertexLightingGLTest { } } - @Test - void faceOnNormalMatchesGeneratedShader() { - GLStateManager.glNormal3f(0.0f, 0.0f, 1.0f); - assertSolverMatchesShader("face-on normal"); + static Stream normals() { + return Stream.of( + Arguments.of("face-on normal", 0.0f, 0.0f, 1.0f, Float.POSITIVE_INFINITY), + // A 0.0104-long normal must stay at the 0.4 light model ambient floor rather than pick up 0.6 per light + Arguments.of("sign normal", 0.0f, 0.0f, -0.010416667f, 0.45f), + Arguments.of("tilted normal", 0.3f, 0.8f, 0.5f, Float.POSITIVE_INFINITY)); } - @Test - void signNormalMatchesGeneratedShader() { - GLStateManager.glNormal3f(0.0f, 0.0f, -0.010416667f); - assertSolverMatchesShader("sign normal"); - assertTrue(factor.x < 0.45f, "a 0.0104-long normal must stay at the 0.4 light model ambient floor rather than pick up 0.6 per light, got " + factor.x); - } - - @Test - void tiltedNormalMatchesGeneratedShader() { - GLStateManager.glNormal3f(0.3f, 0.8f, 0.5f); - assertSolverMatchesShader("tilted normal"); + @ParameterizedTest(name = "{0}") + @MethodSource("normals") + void normalMatchesGeneratedShader(String name, float x, float y, float z, float factorCeiling) { + GLStateManager.glNormal3f(x, y, z); + assertSolverMatchesShader(name); + assertTrue(factor.x < factorCeiling, name + ": factor must stay below " + factorCeiling + ", got " + factor.x); } @Test @@ -206,27 +200,31 @@ class FFPVertexLightingGLTest { assertTrue(factor.x > 1.2f, "normalize restores the unit normal, which must pick up more diffuse than rescale's 0.5-long one, got " + factor.x); } - @Test - void materialEmissionIsNotRepresentable() { - GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION, floats(0.1f, 0.1f, 0.1f, 1.0f)); - assertFalse(FFPVertexLighting.modulatesVertexColor(factor), "emission is additive, so the lit colour is not vertexColor * factor"); + static Stream unrepresentableStates() { + return Stream.of( + unrepresentable("emission is additive, so the lit colour is not vertexColor * factor", + () -> GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION, floats(0.1f, 0.1f, 0.1f, 1.0f))), + unrepresentable("specular is additive, so the lit colour is not vertexColor * factor", () -> { + GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_SPECULAR, floats(1.0f, 1.0f, 1.0f, 1.0f)); + GLStateManager.glMaterialf(GL11.GL_FRONT_AND_BACK, GL11.GL_SHININESS, 16.0f); + GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, floats(1.0f, 1.0f, 1.0f, 1.0f)); + }), + unrepresentable("only AMBIENT_AND_DIFFUSE makes the lit colour scale with the vertex colour", + () -> GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION))); + } + + private static Arguments unrepresentable(String reason, Runnable state) { + return Arguments.of(reason, state); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("unrepresentableStates") + void additiveLightingIsNotRepresentable(String reason, Runnable state) { + state.run(); + assertFalse(FFPVertexLighting.modulatesVertexColor(factor), reason); assertEquals(IDENTITY_FACTOR, factor, "a rejected solve must leave an identity factor"); } - @Test - void specularContributionIsNotRepresentable() { - GLStateManager.glMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_SPECULAR, floats(1.0f, 1.0f, 1.0f, 1.0f)); - GLStateManager.glMaterialf(GL11.GL_FRONT_AND_BACK, GL11.GL_SHININESS, 16.0f); - GLStateManager.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, floats(1.0f, 1.0f, 1.0f, 1.0f)); - assertFalse(FFPVertexLighting.modulatesVertexColor(factor), "specular is additive, so the lit colour is not vertexColor * factor"); - } - - @Test - void nonAmbientAndDiffuseColorMaterialIsNotRepresentable() { - GLStateManager.glColorMaterial(GL11.GL_FRONT_AND_BACK, GL11.GL_EMISSION); - assertFalse(FFPVertexLighting.modulatesVertexColor(factor), "only AMBIENT_AND_DIFFUSE makes the lit colour scale with the vertex colour"); - } - @Test void lightsBeyondTheFfpCountAreIgnored() { assertTrue(FFPVertexLighting.modulatesVertexColor(factor), FFPVertexLightingGLTest::gateState); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/FfpUniformBlockDedupTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/FfpUniformBlockDedupTest.java index 9fc7b3291..5e1ef261d 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/FfpUniformBlockDedupTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/FfpUniformBlockDedupTest.java @@ -11,10 +11,10 @@ import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL31; import java.nio.DoubleBuffer; -import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.List; +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.floats; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -25,12 +25,6 @@ class FfpUniformBlockDedupTest { return GL30.glGetInteger(GL31.GL_UNIFORM_BUFFER_START, FFPUniformBlock.BINDING_POINT); } - private static FloatBuffer floats(float... values) { - final FloatBuffer buf = BufferUtils.createFloatBuffer(values.length); - buf.put(values).flip(); - return buf; - } - private static DoubleBuffer doubles(double... values) { final DoubleBuffer buf = BufferUtils.createDoubleBuffer(values.length); buf.put(values).flip(); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/InstancedFFPDrawGLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/InstancedFFPDrawGLTest.java index fa4e63cc6..92243a6c1 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/InstancedFFPDrawGLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/InstancedFFPDrawGLTest.java @@ -21,6 +21,7 @@ import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0; import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memPutFloat; import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.COLOR_BIT; import static com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags.NORMAL_BIT; +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -100,7 +101,7 @@ class InstancedFFPDrawGLTest { GLSMHooks.blendHandler = new LoggingBlendHandler(); GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 0); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + drainErrors(); assertEquals(List.of("resolve", "flush"), log); } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/LogicOpGLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/LogicOpGLTest.java index a9e55d594..d2808920d 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/LogicOpGLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/LogicOpGLTest.java @@ -36,7 +36,6 @@ class LogicOpGLTest { GLStateManager.glDisable(GL11.GL_BLEND); LogicOpFixture.deleteResources(); FfpFixture.resetFfpState(); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} } @Test @@ -48,7 +47,6 @@ class LogicOpGLTest { assertRgb(op, src); } } - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } @Test diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/WideLineClipGLTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/WideLineClipGLTest.java index 1a85e1674..afaf85c83 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/WideLineClipGLTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/ffp/WideLineClipGLTest.java @@ -5,11 +5,15 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import java.nio.FloatBuffer; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -59,17 +63,18 @@ class WideLineClipGLTest { GLStateManager.glLoadIdentity(); GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); FfpFixture.resetFfpState(); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} } - @Test - void lineEndingBehindTheCameraKeepsItsWidth() { - assertConstantWidth(IN_FRONT, BEHIND); + static Stream linesCrossingTheNearPlane() { + return Stream.of( + Arguments.of("line ending behind the camera", IN_FRONT, BEHIND), + Arguments.of("line starting behind the camera", BEHIND, IN_FRONT)); } - @Test - void lineStartingBehindTheCameraKeepsItsWidth() { - assertConstantWidth(BEHIND, IN_FRONT); + @ParameterizedTest(name = "{0}") + @MethodSource("linesCrossingTheNearPlane") + void lineCrossingTheNearPlaneKeepsItsWidth(String name, float[] start, float[] end) { + assertConstantWidth(start, end); } @Test @@ -144,7 +149,6 @@ class WideLineClipGLTest { final int y = row, count = lit; assertTrue(count > 0 && count <= LINE_WIDTH + 1, () -> "row " + y + " is " + count + " pixels wide"); } - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); } private int[] drawLine(float[] start, float[] end) { diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/loading/ModRedirectorTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/loading/ModRedirectorTest.java index 6470105ef..b790bdfa5 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/loading/ModRedirectorTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/loading/ModRedirectorTest.java @@ -2,6 +2,9 @@ package com.gtnewhorizons.angelica.glsm.loading; import com.gtnewhorizons.angelica.glsm.redirect.GLSMRedirector; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; @@ -12,6 +15,7 @@ import org.objectweb.asm.tree.MethodNode; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; @@ -71,14 +75,16 @@ class ModRedirectorTest { assertEquals(coreExclusions.length + 2, exclusions.size(), exclusions.toString()); } - @Test - void exclusionsCombineCoreLwjgl3AndExtra() { - assertStandardExclusions(redirector().getTransformerExclusions()); + static Stream exclusionLists() { + return Stream.of( + Arguments.of("ModRedirector", redirector().getTransformerExclusions()), + Arguments.of("EarlyRedirectorCore", new EarlyRedirectorCore(EXTRA_EXCLUSION).getTransformerExclusions())); } - @Test - void earlyRedirectorExclusionsCombineCoreLwjgl3AndExtra() { - assertStandardExclusions(new EarlyRedirectorCore(EXTRA_EXCLUSION).getTransformerExclusions()); + @ParameterizedTest(name = "{0}") + @MethodSource("exclusionLists") + void exclusionsCombineCoreLwjgl3AndExtra(String name, String[] exclusions) { + assertStandardExclusions(exclusions); } @Test @@ -86,22 +92,17 @@ class ModRedirectorTest { assertNull(redirector().transform("com.example.TestClass", null, this)); } - @Test - void excludedPrefixReturnsOriginalArray() { - final byte[] original = bytesWithGlCall(); - assertSame(original, redirector().transform(EXTRA_EXCLUSION + ".TestClass", original, this)); + static Stream untouchedClasses() { + return Stream.of( + Arguments.of("extra exclusion prefix", EXTRA_EXCLUSION + ".TestClass", bytesWithGlCall()), + Arguments.of("core exclusion prefix", "org.lwjgl.opengl.TestClass", bytesWithGlCall()), + Arguments.of("no GL call", "com.example.TestClass", bytesWithoutGlCall())); } - @Test - void coreExcludedPrefixReturnsOriginalArray() { - final byte[] original = bytesWithGlCall(); - assertSame(original, redirector().transform("org.lwjgl.opengl.TestClass", original, this)); - } - - @Test - void unchangedClassReturnsOriginalArray() { - final byte[] original = bytesWithoutGlCall(); - assertSame(original, redirector().transform("com.example.TestClass", original, this)); + @ParameterizedTest(name = "{0}") + @MethodSource("untouchedClasses") + void untouchedClassReturnsOriginalArray(String name, String className, byte[] original) { + assertSame(original, redirector().transform(className, original, this)); } @Test diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/profiling/TracyOptionsTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/profiling/TracyOptionsTest.java index 05c9cbda8..1937b5d8e 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/profiling/TracyOptionsTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/profiling/TracyOptionsTest.java @@ -1,6 +1,8 @@ package com.gtnewhorizons.angelica.glsm.profiling; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -8,32 +10,26 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class TracyOptionsTest { - @Test - void resolveBooleanFallsBackToConfigWhenNoOverride() { - assertTrue(TracyOptions.resolveBoolean(null, true)); - assertFalse(TracyOptions.resolveBoolean(null, false)); + @ParameterizedTest(name = "{0}") + @CsvSource(value = { + "no override falls back to config true, null, true, true", + "no override falls back to config false, null, false, false", + "override true wins, true, false, true", + "override false wins, false, true, false" + }, nullValues = "null") + void resolveBoolean(String name, Boolean override, boolean config, boolean expected) { + assertEquals(expected, TracyOptions.resolveBoolean(override, config)); } - @Test - void resolveBooleanOverrideWinsEitherWay() { - assertTrue(TracyOptions.resolveBoolean(Boolean.TRUE, false)); - assertFalse(TracyOptions.resolveBoolean(Boolean.FALSE, true)); - } - - @Test - void resolveMaxSrcLocsFallsBackToConfigWhenNoOverride() { - assertEquals(4096, TracyOptions.resolveMaxSrcLocs(null, 4096)); - } - - @Test - void resolveMaxSrcLocsOverrideWins() { - assertEquals(8192, TracyOptions.resolveMaxSrcLocs(8192, 4096)); - } - - @Test - void resolveMaxSrcLocsClampsBothConfigAndOverrideToTheMinimum() { - assertEquals(16, TracyOptions.resolveMaxSrcLocs(null, 4)); - assertEquals(16, TracyOptions.resolveMaxSrcLocs(1, 4096)); + @ParameterizedTest(name = "{0}") + @CsvSource(value = { + "no override falls back to config, null, 4096, 4096", + "override wins, 8192, 4096, 8192", + "config is clamped to the minimum, null, 4, 16", + "override is clamped to the minimum, 1, 4096, 16" + }, nullValues = "null") + void resolveMaxSrcLocs(String name, Integer override, int config, int expected) { + assertEquals(expected, TracyOptions.resolveMaxSrcLocs(override, config)); } @Test diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/IndexedDrawBatchBuilderTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/IndexedDrawBatchBuilderTest.java index aed8397db..853b9faf8 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/IndexedDrawBatchBuilderTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/IndexedDrawBatchBuilderTest.java @@ -62,25 +62,6 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture { MemoryUtilities.memFree(read); } - @Test - void compileAndExecute_firesLiveAndBakes() { - final int vbo = defaultQuadVbo(); - final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2); - final int vao = setupPosColorVao(vbo); - - final int list = newList(); - GLStateManager.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE); - GLStateManager.glBindVertexArray(vao); - GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ebo); - GLStateManager.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L); - GLStateManager.glBindVertexArray(0); - GLStateManager.glEndList(); - - final BatchedIndexedDrawCmd cmd = firstBatchedCmd(list); - assertNotNull(cmd); - assertEquals(3, cmd.getIndexCount()); - } - @Test void quads_triangulateAtRecord() { final int vbo = defaultQuadVbo(); @@ -457,11 +438,6 @@ class IndexedDrawBatchBuilderTest extends DisplayListTestFixture { assertNotEquals(cmds.get(0).getSharedVAO(), cmds.get(1).getSharedVAO(), "VAOs from separate batches must be distinct"); } - @Test - void nullListProbeCompiles() { - assertNull(DisplayListManager.getDisplayList(0)); - } - @Test void multiLayoutDraws_emitInInsertionOrder() { // Two distinct layouts. The builder uses a LinkedHashMap, so the first diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/IndexedDrawDeleteSafetyTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/IndexedDrawDeleteSafetyTest.java index bc4d2bbf6..453c94b76 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/IndexedDrawDeleteSafetyTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/IndexedDrawDeleteSafetyTest.java @@ -1,24 +1,30 @@ package com.gtnewhorizons.angelica.glsm.recording; import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import com.gtnewhorizons.angelica.glsm.recording.commands.IndexedDrawBatch; import com.gtnewhorizons.angelica.glsm.recording.support.DisplayListTestFixture; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL20; +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors; import static org.junit.jupiter.api.Assertions.assertEquals; class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture { - @Test - void vaoEboMapSweep_clearsStaleEntryOnForeignVao() { + @ParameterizedTest(name = "replayed before delete={0}") + @ValueSource(booleans = {false, true}) + void listDelete_clearsSharedEboFromForeignVao(boolean replayFirst) { final int vbo = defaultQuadVbo(); final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2, 3); final int vao = setupPosColorVao(vbo); final int list = recordSingleIndexedDraw(vao, ebo, GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT); + if (replayFirst) compiledOf(list).render(); + final IndexedDrawBatch batch = firstBatch(list); final int sharedEbo = batch.getSharedEBO(); @@ -31,32 +37,13 @@ class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture { untrackList(list); GLStateManager.glBindVertexArray(foreignVao); - assertEquals(0, GLStateManager.getBoundEBO(), "vaoEboMap still references the freed sharedEBO — non-gen crash imminent"); - GLStateManager.glBindVertexArray(0); - } + assertEquals(0, GLStateManager.getBoundEBO(), "vaoEboMap still references the freed sharedEBO - non-gen crash imminent"); - @Test - void bindBufferCycle_afterListDelete_isGlNoError() { - final int vbo = defaultQuadVbo(); - final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2, 3); - final int vao = setupPosColorVao(vbo); - final int list = recordSingleIndexedDraw(vao, ebo, GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT); - - final int sharedEbo = firstBatch(list).getSharedEBO(); - final int foreignVao = newVao(); - GLStateManager.glBindVertexArray(foreignVao); - GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, sharedEbo); - GLStateManager.glBindVertexArray(0); - - GLStateManager.glDeleteLists(list, 1); - untrackList(list); - - GLStateManager.glBindVertexArray(foreignVao); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + drainErrors(); final int prevEbo = GLStateManager.getBoundEBO(); GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo); final int err = GL11.glGetError(); - assertEquals(GL11.GL_NO_ERROR, err, () -> "bindBuffer(prevEbo=" + prevEbo + ") hit GL 0x" + Integer.toHexString(err) + " — cache carried a freed id through a VAO rebind"); + assertEquals(GL11.GL_NO_ERROR, err, () -> "bindBuffer(prevEbo=" + prevEbo + ") hit GL 0x" + Integer.toHexString(err) + " - cache carried a freed id through a VAO rebind"); GLStateManager.glBindVertexArray(0); } @@ -85,8 +72,8 @@ class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture { @Test void glDeleteProgram_zero_isNoOpAndLeavesActiveProgram() { - final int prog = linkMinimalProgram(); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + final int prog = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS); + drainErrors(); GLStateManager.glUseProgram(prog); assertEquals(prog, GLStateManager.getActiveProgram()); @@ -97,13 +84,13 @@ class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture { GLStateManager.glUseProgram(0); GLStateManager.glDeleteProgram(prog); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + drainErrors(); } @Test void activeProgram_retainedAfterDelete_perGlSpec() { - final int prog = linkMinimalProgram(); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + final int prog = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS); + drainErrors(); GLStateManager.glUseProgram(prog); assertEquals(prog, GLStateManager.getActiveProgram()); @@ -114,38 +101,6 @@ class IndexedDrawDeleteSafetyTest extends DisplayListTestFixture { GLStateManager.glUseProgram(0); assertEquals(0, GLStateManager.getActiveProgram(), "activeProgram clears once the deleted program is unbound"); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + drainErrors(); } - - private static int linkMinimalProgram() { - final int vert = GLStateManager.glCreateShader(GL20.GL_VERTEX_SHADER); - GLStateManager.glShaderSource(vert, MIN_VERT_SRC); - GLStateManager.glCompileShader(vert); - if (GL20.glGetShaderi(vert, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) { - throw new AssertionError("vertex shader failed to compile: " + GL20.glGetShaderInfoLog(vert, 4096)); - } - - final int frag = GLStateManager.glCreateShader(GL20.GL_FRAGMENT_SHADER); - GLStateManager.glShaderSource(frag, MIN_FRAG_SRC); - GLStateManager.glCompileShader(frag); - if (GL20.glGetShaderi(frag, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) { - throw new AssertionError("fragment shader failed to compile: " + GL20.glGetShaderInfoLog(frag, 4096)); - } - - final int prog = GLStateManager.glCreateProgram(); - GLStateManager.glAttachShader(prog, vert); - GLStateManager.glAttachShader(prog, frag); - GLStateManager.glLinkProgram(prog); - if (GL20.glGetProgrami(prog, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) { - throw new AssertionError("program failed to link: " + GL20.glGetProgramInfoLog(prog, 4096)); - } - GLStateManager.glDetachShader(prog, vert); - GLStateManager.glDetachShader(prog, frag); - GLStateManager.glDeleteShader(vert); - GLStateManager.glDeleteShader(frag); - return prog; - } - - private static final String MIN_VERT_SRC = "#version 330 core\nvoid main(){gl_Position=vec4(0.0);}"; - private static final String MIN_FRAG_SRC = "#version 330 core\nout vec4 oC;void main(){oC=vec4(1.0);}"; } diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/IndexedDrawReplayTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/IndexedDrawReplayTest.java index 2b8b3e85a..446b87532 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/IndexedDrawReplayTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/IndexedDrawReplayTest.java @@ -15,6 +15,7 @@ import org.lwjgl.opengl.GL15; import java.nio.ByteBuffer; import java.util.List; +import static com.gtnewhorizons.angelica.glsm.GlTestSupport.drainErrors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -36,7 +37,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture { GL11.glViewport(0, 0, 16, 16); GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glClear(GL11.GL_COLOR_BUFFER_BIT); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + drainErrors(); final int query = GL15.glGenQueries(); GL15.glBeginQuery(GL15.GL_SAMPLES_PASSED, query); @@ -92,7 +93,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture { GLStateManager.glBindVertexArray(distractorVao); GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + drainErrors(); compiledOf(list).render(); @@ -103,35 +104,6 @@ class IndexedDrawReplayTest extends DisplayListTestFixture { GLStateManager.glBindVertexArray(0); } - @Test - void replayThenDelete_thenBindBufferCycle_isGlNoError() { - final int vbo = defaultQuadVbo(); - final int ebo = uploadEbo(GL11.GL_UNSIGNED_SHORT, 0, 1, 2, 3); - final int vao = setupPosColorVao(vbo); - final int list = recordSingleIndexedDraw(vao, ebo, GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT); - - compiledOf(list).render(); - - - final int sharedEbo = firstBatch(list).getSharedEBO(); - final int foreignVao = newVao(); - GLStateManager.glBindVertexArray(foreignVao); - GLStateManager.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, sharedEbo); - GLStateManager.glBindVertexArray(0); - - GLStateManager.glDeleteLists(list, 1); - untrackList(list); - - GLStateManager.glBindVertexArray(foreignVao); - assertEquals(0, GLStateManager.getBoundEBO(), "batched EBO id survived list delete in vaoEboMap — non-gen crash imminent"); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} - final int prevEbo = GLStateManager.getBoundEBO(); - GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, prevEbo); - final int err = GL11.glGetError(); - assertEquals(GL11.GL_NO_ERROR, err, () -> "post-delete bindBuffer(" + prevEbo + ") hit GL 0x" + Integer.toHexString(err)); - GLStateManager.glBindVertexArray(0); - } - @Test void execute_doesNotRestoreVAO_backToBackDraws() { final int vbo = defaultQuadVbo(); @@ -153,7 +125,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture { final int distractor = newVao(); GLStateManager.glBindVertexArray(distractor); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + drainErrors(); compiledOf(list).render(); @@ -229,7 +201,7 @@ class IndexedDrawReplayTest extends DisplayListTestFixture { GLStateManager.glDrawElements(GL11.GL_QUADS, 4, GL11.GL_UNSIGNED_SHORT, 0L); GLStateManager.glEndList(); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + drainErrors(); compiledOf(list).render(); compiledOf(list).render(); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/RecordingDataStructuresTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/RecordingDataStructuresTest.java index 396bfafa3..fc2757ec1 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/RecordingDataStructuresTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/RecordingDataStructuresTest.java @@ -3,10 +3,8 @@ package com.gtnewhorizons.angelica.glsm.recording; import com.gtnewhorizon.gtnhlib.client.renderer.DirectTessellator; import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat; import com.gtnewhorizons.angelica.glsm.GLCompatTest; -import com.gtnewhorizons.angelica.glsm.DisplayListManager; import com.gtnewhorizons.angelica.glsm.GLCompatTest; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import it.unimi.dsi.fastutil.ints.Int2IntMap; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.lwjgl.opengl.GL11; @@ -33,37 +31,6 @@ class RecordingDataStructuresTest { } } - @Test - void testCompiledDisplayListCreation() { - // Create a display list with PushMatrix, Scale, PopMatrix - testList = GL11.glGenLists(1); - GLStateManager.glNewList(testList, GL11.GL_COMPILE); - - GLStateManager.glPushMatrix(); - GLStateManager.glScalef(2.0f, 2.0f, 2.0f); - GLStateManager.glPopMatrix(); - - GLStateManager.glEndList(); - - // Verify the compiled display list - CompiledDisplayList compiled = DisplayListManager.getDisplayList(testList); - assertNotNull(compiled, "Display list should be compiled"); - assertNotNull(compiled.getCommandBuffer(), "Command buffer should not be null"); - - // Get command counts to verify structure - Int2IntMap counts = compiled.getCommandCounts(); - - // Should have PushMatrix and PopMatrix - assertEquals(1, counts.getOrDefault(GLCommand.PUSH_MATRIX, 0), "Should have 1 PushMatrix"); - assertEquals(1, counts.getOrDefault(GLCommand.POP_MATRIX, 0), "Should have 1 PopMatrix"); - - // Scale should be collapsed into a MultMatrix (optimizer collapses transforms) - // Or if in debug mode, might have individual commands - just check we have some transforms - int transformCount = counts.getOrDefault(GLCommand.MULT_MATRIX, 0) - + counts.getOrDefault(GLCommand.SCALE, 0); - assertTrue(transformCount >= 0, "Should have transform commands"); - } - @Test void testImmediateModeRecorder() { DirectTessellator recorder = ImmediateModeRecorder.getInternalTessellator(); diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/commands/AttribLayoutKeyTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/commands/AttribLayoutKeyTest.java index a92712b82..5dfdf30bc 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/commands/AttribLayoutKeyTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/recording/commands/AttribLayoutKeyTest.java @@ -1,8 +1,13 @@ package com.gtnewhorizons.angelica.glsm.recording.commands; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL11; +import java.util.stream.Stream; + import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -15,59 +20,50 @@ class AttribLayoutKeyTest { return out; } - @Test - void standardPosColorUvNormal_isAlignedAndPacked() { - final AttribLayoutKey k = new AttribLayoutKey( - new int[]{0, 1, 2, 4}, - new int[]{3, 4, 2, 3}, - new int[]{GL11.GL_FLOAT, GL11.GL_UNSIGNED_BYTE, GL11.GL_FLOAT, GL11.GL_BYTE}, - new boolean[]{false, true, false, true}); - assertArrayEquals(new int[]{0, 12, 16, 24}, offsetsOf(k)); - assertEquals(28, k.stride()); + static Stream layouts() { + return Stream.of( + Arguments.of("standard pos/color/uv/normal is aligned and packed", + new AttribLayoutKey( + new int[]{0, 1, 2, 4}, + new int[]{3, 4, 2, 3}, + new int[]{GL11.GL_FLOAT, GL11.GL_UNSIGNED_BYTE, GL11.GL_FLOAT, GL11.GL_BYTE}, + new boolean[]{false, true, false, true}), + new int[]{0, 12, 16, 24}, 28), + Arguments.of("pos/uv/normal3 rounds the tail stride to 24", + new AttribLayoutKey( + new int[]{0, 2, 4}, + new int[]{3, 2, 3}, + new int[]{GL11.GL_FLOAT, GL11.GL_FLOAT, GL11.GL_BYTE}, + new boolean[]{false, false, true}), + new int[]{0, 12, 20}, 24), + Arguments.of("byte attrib in the middle rounds the next offset", + new AttribLayoutKey( + new int[]{0, 4, 2}, + new int[]{3, 3, 2}, + new int[]{GL11.GL_FLOAT, GL11.GL_BYTE, GL11.GL_FLOAT}, + new boolean[]{false, true, false}), + new int[]{0, 12, 16}, 24), + Arguments.of("all-byte layout packs tight", + new AttribLayoutKey( + new int[]{0, 1, 2}, + new int[]{3, 4, 2}, + new int[]{GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE, GL11.GL_BYTE}, + new boolean[]{true, true, false}), + new int[]{0, 3, 7}, 9), + Arguments.of("shorts after a byte round offset and stride to even", + new AttribLayoutKey( + new int[]{0, 1}, + new int[]{1, 2}, + new int[]{GL11.GL_BYTE, GL11.GL_SHORT}, + new boolean[]{true, false}), + new int[]{0, 2}, 6)); } - @Test - void posUvNormal3_roundsTailStrideTo24() { - final AttribLayoutKey k = new AttribLayoutKey( - new int[]{0, 2, 4}, - new int[]{3, 2, 3}, - new int[]{GL11.GL_FLOAT, GL11.GL_FLOAT, GL11.GL_BYTE}, - new boolean[]{false, false, true}); - assertArrayEquals(new int[]{0, 12, 20}, offsetsOf(k)); - assertEquals(24, k.stride()); - } - - @Test - void interiorHazardOrder_roundsMidAttribOffset() { - final AttribLayoutKey k = new AttribLayoutKey( - new int[]{0, 4, 2}, - new int[]{3, 3, 2}, - new int[]{GL11.GL_FLOAT, GL11.GL_BYTE, GL11.GL_FLOAT}, - new boolean[]{false, true, false}); - assertArrayEquals(new int[]{0, 12, 16}, offsetsOf(k)); - assertEquals(24, k.stride()); - } - - @Test - void allByteLayout_packsTight() { - final AttribLayoutKey k = new AttribLayoutKey( - new int[]{0, 1, 2}, - new int[]{3, 4, 2}, - new int[]{GL11.GL_BYTE, GL11.GL_UNSIGNED_BYTE, GL11.GL_BYTE}, - new boolean[]{true, true, false}); - assertArrayEquals(new int[]{0, 3, 7}, offsetsOf(k)); - assertEquals(9, k.stride()); - } - - @Test - void shortsFollowingByte_roundToEvenAndStrideToEven() { - final AttribLayoutKey k = new AttribLayoutKey( - new int[]{0, 1}, - new int[]{1, 2}, - new int[]{GL11.GL_BYTE, GL11.GL_SHORT}, - new boolean[]{true, false}); - assertArrayEquals(new int[]{0, 2}, offsetsOf(k)); - assertEquals(6, k.stride()); + @ParameterizedTest(name = "{0}") + @MethodSource("layouts") + void offsetsAndStride(String name, AttribLayoutKey key, int[] offsets, int stride) { + assertArrayEquals(offsets, offsetsOf(key)); + assertEquals(stride, key.stride()); } @Test diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirectorTest.java b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirectorTest.java index e0e7d6e29..5ce18ad02 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirectorTest.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirectorTest.java @@ -5,6 +5,8 @@ import com.gtnewhorizons.angelica.config.SystemProperties.UnmappedGLMode; import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Handle; import org.objectweb.asm.Opcodes; @@ -16,7 +18,6 @@ import org.objectweb.asm.tree.InvokeDynamicInsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; -import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -32,19 +33,8 @@ class GLSMRedirectorTest { private static final String GLSM = GLSMRedirector.getTargetClassName(); - @SuppressWarnings("unchecked") - private static Set seenSet(String field) { - try { - final Field f = GLSMRedirector.class.getDeclaredField(field); - f.setAccessible(true); - return (Set) f.get(null); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - private static Set unmappedSeen() { return seenSet("UNMAPPED_SEEN"); } - private static Set debugSeen() { return seenSet("DEBUG_SEEN"); } + private static Set unmappedSeen() { return Reflect.getStatic(GLSMRedirector.class, "UNMAPPED_SEEN"); } + private static Set debugSeen() { return Reflect.getStatic(GLSMRedirector.class, "DEBUG_SEEN"); } @BeforeEach void reset() { @@ -112,35 +102,25 @@ class GLSMRedirectorTest { return (Handle) dyn.bsmArgs[1]; } - @Test - void invokeDynamicGL11HandleIsRedirected() { - final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11", "glPopMatrix", "()V", false); - final ClassNode cn = makeClassWithInvokeDynamic(impl); + @ParameterizedTest(name = "{0}") + @CsvSource({ + "GL11, org/lwjgl/opengl/GL11, glPopMatrix, ()V, glPopMatrix", + "extension class with a renamed target, org/lwjgl/opengl/EXTBlendFuncSeparate, glBlendFuncSeparateEXT, (IIII)V, tryBlendFuncSeparate", + "GLxxC variant, org/lwjgl/opengl/GL11C, glDrawArrays, (III)V, glDrawArrays" + }) + void invokeDynamicHandleIsRedirected(String name, String owner, String method, String desc, String expectedName) { + final ClassNode cn = makeClassWithInvokeDynamic(new Handle(Opcodes.H_INVOKESTATIC, owner, method, desc, false)); final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn); assertTrue(changed); final Handle result = getResultHandle(cn); assertEquals(GLSM, result.getOwner()); - assertEquals("glPopMatrix", result.getName()); - assertEquals("()V", result.getDesc()); + assertEquals(expectedName, result.getName()); + assertEquals(desc, result.getDesc()); assertEquals(Opcodes.H_INVOKESTATIC, result.getTag()); } - @Test - void invokeDynamicNamedClassHandleIsRedirected() { - final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/EXTBlendFuncSeparate", "glBlendFuncSeparateEXT", "(IIII)V", false); - final ClassNode cn = makeClassWithInvokeDynamic(impl); - - final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn); - - assertTrue(changed); - final Handle result = getResultHandle(cn); - assertEquals(GLSM, result.getOwner()); - assertEquals("tryBlendFuncSeparate", result.getName()); - assertEquals("(IIII)V", result.getDesc()); - } - @Test void invokeDynamicNonGLHandleIsNotTouched() { final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); @@ -188,6 +168,7 @@ class GLSMRedirectorTest { @Test void nonAwareDebugCallbackIsRedirectedNotPassedThrough() { assertRedirected("org/lwjgl/opengl/GL43", "glDebugMessageCallback", "(Lorg/lwjgl/opengl/KHRDebugCallback;)V", "glDebugMessageCallback"); + assertRedirected("org/lwjgl/opengl/KHRDebug", "glDebugMessageCallback", "(Lorg/lwjgl/opengl/KHRDebugCallback;)V", "glDebugMessageCallback"); assertTrue(debugSeen().isEmpty()); assertTrue(unmappedSeen().isEmpty()); } @@ -240,54 +221,16 @@ class GLSMRedirectorTest { assertTrue(unmappedSeen().isEmpty()); } - @Test - void newlyMappedFramebufferAliasIsRedirected() { - final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/EXTFramebufferObject", "glBindFramebufferEXT", "(II)V"); - - final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn); - - assertTrue(changed); - assertEquals(GLSM, firstCall(cn).owner); - assertEquals("glBindFramebuffer", firstCall(cn).name); - assertTrue(unmappedSeen().isEmpty()); - } - - @Test - void arbGenerateMipmapAliasIsRedirected() { - final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/ARBFramebufferObject", "glGenerateMipmap", "(I)V"); - - final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn); - - assertTrue(changed); - assertEquals(GLSM, firstCall(cn).owner); - assertEquals("glGenerateMipmap", firstCall(cn).name); - assertEquals("(I)V", firstCall(cn).desc); - assertTrue(unmappedSeen().isEmpty()); - } - - @Test - void extGenerateMipmapAliasIsRedirected() { - final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/EXTFramebufferObject", "glGenerateMipmapEXT", "(I)V"); - - final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn); - - assertTrue(changed); - assertEquals(GLSM, firstCall(cn).owner); - assertEquals("glGenerateMipmap", firstCall(cn).name); - assertEquals("(I)V", firstCall(cn).desc); - assertTrue(unmappedSeen().isEmpty()); - } - - @Test - void newlyMappedReadPixelsIsRedirected() { - final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/GL11", "glReadPixels", "(IIIIIILjava/nio/IntBuffer;)V"); - - final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn); - - assertTrue(changed); - assertEquals(GLSM, firstCall(cn).owner); - assertEquals("glReadPixels", firstCall(cn).name); - assertTrue(unmappedSeen().isEmpty()); + @ParameterizedTest(name = "{1}") + @CsvSource(delimiter = '|', value = { + "org/lwjgl/opengl/EXTFramebufferObject | glBindFramebufferEXT | (II)V | glBindFramebuffer", + "org/lwjgl/opengl/ARBFramebufferObject | glGenerateMipmap | (I)V | glGenerateMipmap", + "org/lwjgl/opengl/EXTFramebufferObject | glGenerateMipmapEXT | (I)V | glGenerateMipmap", + "org/lwjgl/opengl/GL11 | glReadPixels | (IIIIIILjava/nio/IntBuffer;)V | glReadPixels", + "org/lwjgl/opengl/ARBOcclusionQuery | glGenQueriesARB | (Ljava/nio/IntBuffer;)V | glGenQueries" + }) + void aliasIsRedirected(String owner, String name, String desc, String expectedName) { + assertEquals(desc, assertRedirected(owner, name, desc, expectedName).desc); } @Test @@ -299,47 +242,29 @@ class GLSMRedirectorTest { }); } - @Test - void newlyMappedOcclusionQueryAliasIsRedirected() { - final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/ARBOcclusionQuery", "glGenQueriesARB", "(Ljava/nio/IntBuffer;)V"); - - final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn); - - assertTrue(changed); - assertEquals(GLSM, firstCall(cn).owner); - assertEquals("glGenQueries", firstCall(cn).name); - assertTrue(unmappedSeen().isEmpty()); - } - @Test void utilCheckGLErrorIsRedirected() throws Exception { assertRedirectedToDeclaredTarget(new String[][]{{"org/lwjgl/opengl/Util", "checkGLError", "()V"}}); } @Test - void indexedGettersHaveRealTargets() throws Exception { - final Class glsm = Class.forName(GLSM.replace('/', '.'), false, getClass().getClassLoader()); - assertDoesNotThrow(() -> glsm.getDeclaredMethod("glGetInteger", int.class, int.class), "LWJGL2 GL30.glGetInteger(pname, index) redirects here"); - assertDoesNotThrow(() -> glsm.getDeclaredMethod("glGetIntegeri", int.class, int.class), "LWJGL3 GL31C.glGetIntegeri(pname, index) redirects here"); - assertDoesNotThrow(() -> glsm.getDeclaredMethod("glGetIntegeri_v", int.class, int.class, java.nio.IntBuffer.class)); - assertDoesNotThrow(() -> glsm.getDeclaredMethod("glGetBooleani_v", int.class, int.class, java.nio.ByteBuffer.class)); + void indexedGettersHaveDeclaredTargets() throws Exception { + assertRedirectedToDeclaredTarget(new String[][]{ + {"org/lwjgl/opengl/GL30", "glGetInteger", "(II)I"}, + {"org/lwjgl/opengl/GL31C", "glGetIntegeri", "(II)I"}, + {"org/lwjgl/opengl/GL30", "glGetIntegeri_v", "(IILjava/nio/IntBuffer;)V"}, + {"org/lwjgl/opengl/GL30", "glGetBooleani_v", "(IILjava/nio/ByteBuffer;)V"}, + }); } - @Test - void indexedGettersAreRedirected() { - assertRedirected("org/lwjgl/opengl/GL30", "glGetInteger", "(II)I", "glGetInteger"); - assertRedirected("org/lwjgl/opengl/GL31C", "glGetIntegeri", "(II)I", "glGetIntegeri"); - assertRedirected("org/lwjgl/opengl/GL30", "glGetIntegeri_v", "(IILjava/nio/IntBuffer;)V", "glGetIntegeri_v"); - assertRedirected("org/lwjgl/opengl/GL30", "glGetBooleani_v", "(IILjava/nio/ByteBuffer;)V", "glGetBooleani_v"); - } - - private void assertRedirected(String owner, String name, String desc, String expectedName) { + private MethodInsnNode assertRedirected(String owner, String name, String desc, String expectedName) { final ClassNode cn = makeClassWithMethodCall(owner, name, desc); final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn); assertTrue(changed, owner + "." + name + " should redirect"); assertEquals(GLSM, firstCall(cn).owner); assertEquals(expectedName, firstCall(cn).name); assertTrue(unmappedSeen().isEmpty(), owner + "." + name + " should not be recorded"); + return firstCall(cn); } private static boolean glsmDeclares(String name, String desc) throws ClassNotFoundException { @@ -465,20 +390,6 @@ class GLSMRedirectorTest { assertRedirected("org/lwjgl/opengl/APPLEVertexArrayObject", "glGenVertexArraysAPPLE", "()I", "glGenVertexArrays"); } - @Test - void invokeDynamicGLCVariantIsRedirected() { - final Handle impl = new Handle(Opcodes.H_INVOKESTATIC, "org/lwjgl/opengl/GL11C", "glDrawArrays", "(III)V", false); - final ClassNode cn = makeClassWithInvokeDynamic(impl); - - final boolean changed = new GLSMRedirector().transformClassNode("com.example.TestClass", cn); - - assertTrue(changed); - final Handle result = getResultHandle(cn); - assertEquals(GLSM, result.getOwner()); - assertEquals("glDrawArrays", result.getName()); - assertEquals("(III)V", result.getDesc()); - } - private static final String AWARE_SHIM = "com/gtnewhorizons/angelica/sdlgpu/compat/AwareGLDebugShim"; private static final String CALLBACK_I = "Lorg/lwjgl/opengl/GLDebugMessageCallbackI;"; @@ -502,15 +413,6 @@ class GLSMRedirectorTest { assertEquals("glDebugMessageCallback", firstCall(cn).name); } - @Test - void nonAwareDebugMessageCallbackTargetsGLStateManager() { - final ClassNode cn = makeClassWithMethodCall("org/lwjgl/opengl/KHRDebug", "glDebugMessageCallback", "(Lorg/lwjgl/opengl/KHRDebugCallback;)V"); - - assertTrue(new GLSMRedirector().transformClassNode("com.example.TestClass", cn, false)); - - assertEquals(GLSM, firstCall(cn).owner); - } - @Test void awareGLUtilSetupDebugMessageCallbackIsRedirected() { for (String desc : new String[]{"()Lorg/lwjgl/system/Callback;", "(Ljava/io/PrintStream;)Lorg/lwjgl/system/Callback;"}) { diff --git a/glsm/src/test/java/com/gtnewhorizons/angelica/util/GLSMUtil.java b/glsm/src/test/java/com/gtnewhorizons/angelica/util/GLSMUtil.java index 6f7a8766f..e1137c5da 100644 --- a/glsm/src/test/java/com/gtnewhorizons/angelica/util/GLSMUtil.java +++ b/glsm/src/test/java/com/gtnewhorizons/angelica/util/GLSMUtil.java @@ -5,6 +5,7 @@ import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL14; import java.nio.ByteBuffer; import java.nio.FloatBuffer; @@ -16,14 +17,15 @@ import java.util.stream.IntStream; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; public class GLSMUtil { - // Cached GL limits (queried once at class load time) - private static final int MAX_CLIP_PLANES = GL11.glGetInteger(GL11.GL_MAX_CLIP_PLANES); - private static final int MAX_LIGHTS = GL11.glGetInteger(GL11.GL_MAX_LIGHTS); - private static final int MAX_TEXTURE_UNITS = GL11.glGetInteger(GL13.GL_MAX_TEXTURE_UNITS); + // Compat-only pnames, so they load with resetGLState() and not with the class: a core driver answers GL_INVALID_ENUM + private static final class CompatLimits { + static final int MAX_CLIP_PLANES = GL11.glGetInteger(GL11.GL_MAX_CLIP_PLANES); + static final int MAX_LIGHTS = GL11.glGetInteger(GL11.GL_MAX_LIGHTS); + static final int MAX_TEXTURE_UNITS = GL11.glGetInteger(GL13.GL_MAX_TEXTURE_UNITS); + } // MAP1 evaluator constants for DRY loops private static final int[] MAP1_STATES = { @@ -76,13 +78,25 @@ public class GLSMUtil { GL11.GL_FOG_MODE, GL11.GL_FOG_INDEX, // Also removed from core profile GL11.GL_POINT_SMOOTH, GL11.GL_POLYGON_STIPPLE, - GL11.GL_INDEX_LOGIC_OP, GL11.GL_AUTO_NORMAL)); + GL11.GL_INDEX_LOGIC_OP, GL11.GL_AUTO_NORMAL, + GL13.GL_CLIENT_ACTIVE_TEXTURE, + GL11.GL_VERTEX_ARRAY, GL11.GL_NORMAL_ARRAY, GL11.GL_COLOR_ARRAY, GL11.GL_TEXTURE_COORD_ARRAY, + GL11.GL_INDEX_ARRAY, GL11.GL_EDGE_FLAG_ARRAY, GL14.GL_SECONDARY_COLOR_ARRAY, GL14.GL_FOG_COORDINATE_ARRAY, + GL11.GL_VERTEX_ARRAY_SIZE, GL11.GL_VERTEX_ARRAY_TYPE, GL11.GL_VERTEX_ARRAY_STRIDE)); for (int cap : MAP1_STATES) caps.add(cap); for (int cap : MAP2_STATES) caps.add(cap); return caps; } + // A core driver rejects these caps with GL_INVALID_ENUM. Only that error is dropped: one already pending is the test's. + private static boolean driverBoolean(int glCap, boolean isEnabled) { + assertEquals(GL11.GL_NO_ERROR, GL11.glGetError(), "GL error pending before the driver query"); + final boolean value = isEnabled ? GL11.glIsEnabled(glCap) : GL11.glGetBoolean(glCap); + while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + return value; + } + private static boolean isFFPOnly(int glCap) { return FFP_ONLY_CAPS.contains(glCap); } @@ -94,7 +108,7 @@ public class GLSMUtil { public static void verifyIsEnabled(int glCap, boolean expected, String message) { if (isFFPOnly(glCap)) { assertAll(message, - () -> assertFalse(GL11.glIsEnabled(glCap), "GL State Mismatch - cache-only cap reached the driver"), + () -> assertFalse(driverBoolean(glCap, true), "GL State Mismatch - cache-only cap reached the driver"), () -> assertEquals(expected, GLStateManager.glIsEnabled(glCap), "GLSM State Mismatch") ); } else { @@ -112,7 +126,7 @@ public class GLSMUtil { public static void verifyState(int glCap, boolean expected, String message) { if (isFFPOnly(glCap)) { assertAll(message, - () -> assertFalse(GL11.glGetBoolean(glCap), "GL State Mismatch - cache-only cap reached the driver"), + () -> assertFalse(driverBoolean(glCap, false), "GL State Mismatch - cache-only cap reached the driver"), () -> assertEquals(expected, GLStateManager.glGetBoolean(glCap), "GLSM State Mismatch") ); } else { @@ -208,22 +222,6 @@ public class GLSMUtil { } } - public static void verifyNotDefaultState(int glCap, float[] expected, String message) { - if (isFFPOnly(glCap)) { - GLStateManager.glGetFloat(glCap, (FloatBuffer) glsmFloatBuffer.clear()); - IntStream.range(0, expected.length).forEach(i -> - assertNotEquals(expected[i], glsmFloatBuffer.get(i), 0.0001f, message + " - GLSM State Mismatch: " + i) - ); - } else { - GL11.glGetFloat(glCap, (FloatBuffer) glFloatBuffer.clear()); - GLStateManager.glGetFloat(glCap, (FloatBuffer) glsmFloatBuffer.clear()); - IntStream.range(0, expected.length).forEach(i -> assertAll(message, - () -> assertNotEquals(expected[i], glFloatBuffer.get(i), 0.0001f, "GL State Mismatch: " + i), - () -> assertNotEquals(expected[i], glsmFloatBuffer.get(i), 0.0001f, "GLSM State Mismatch: " + i) - )); - } - } - public static void verifyState(int glCap, boolean[] expected) { verifyState(glCap, expected, "Boolean State Mismatch"); } @@ -275,7 +273,7 @@ public class GLSMUtil { GLStateManager.disableBlend(); // Disable all clip planes (using cached MAX_CLIP_PLANES) - for(int i = 0; i < MAX_CLIP_PLANES; i++) { + for(int i = 0; i < CompatLimits.MAX_CLIP_PLANES; i++) { GLStateManager.glDisable(GL11.GL_CLIP_PLANE0 + i); } @@ -290,7 +288,7 @@ public class GLSMUtil { GLStateManager.disableLighting(); // Disable all lights (using cached MAX_LIGHTS) - for(int i = 0; i < MAX_LIGHTS; i++) { + for(int i = 0; i < CompatLimits.MAX_LIGHTS; i++) { GLStateManager.disableLight(i); } @@ -330,7 +328,7 @@ public class GLSMUtil { GLStateManager.glDisable(GL11.GL_STENCIL_TEST); // Reset texture state for all units (using cached MAX_TEXTURE_UNITS) - for(int i = 0; i < MAX_TEXTURE_UNITS; i++) { + for(int i = 0; i < CompatLimits.MAX_TEXTURE_UNITS; i++) { GLStateManager.glActiveTexture(GL13.GL_TEXTURE0 + i); GLStateManager.glDisable(GL11.GL_TEXTURE_1D); GLStateManager.disableTexture(); // GL_TEXTURE_2D @@ -383,9 +381,9 @@ public class GLSMUtil { public static void setClientState(int glCap, boolean state) { if (state) { - GL11.glEnableClientState(glCap); + GLStateManager.glEnableClientState(glCap); } else { - GL11.glDisableClientState(glCap); + GLStateManager.glDisableClientState(glCap); } } } diff --git a/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/GLSMCoreExtension.java b/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/GLSMCoreExtension.java index f0eff4aae..4ca9510ea 100644 --- a/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/GLSMCoreExtension.java +++ b/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/GLSMCoreExtension.java @@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.glsm; import com.gtnewhorizons.angelica.config.SystemProperties; import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager; import com.gtnewhorizons.angelica.glsm.hooks.GLSMInitConfig; +import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; @@ -14,12 +15,13 @@ import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.PixelFormat; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL; /** * 3.3 core forward-compatible context. FFP emulation stays enabled, so per-test isolation only deactivates it. */ -public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback, ExtensionContext.Store.CloseableResource { +public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback, ExtensionContext.Store.CloseableResource { private static boolean started = false; private static Throwable startFailure; @@ -75,6 +77,13 @@ public class GLSMCoreExtension implements BeforeAllCallback, BeforeEachCallback, } } + @Override + public void afterEach(ExtensionContext context) { + final int error = GL11.glGetError(); + while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + assertEquals(GL11.GL_NO_ERROR, error, () -> "GL Error: 0x" + Integer.toHexString(error)); + } + @Override public void close() { if (Display.isCreated()) Display.destroy(); diff --git a/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/GlTestSupport.java b/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/GlTestSupport.java new file mode 100644 index 000000000..a4247160b --- /dev/null +++ b/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/GlTestSupport.java @@ -0,0 +1,82 @@ +package com.gtnewhorizons.angelica.glsm; + +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL20; + +import java.nio.FloatBuffer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public final class GlTestSupport { + + public static final String TRIVIAL_VS = "#version 330 core\nvoid main() { gl_Position = vec4(0.0); }\n"; + public static final String TRIVIAL_FS = "#version 330 core\nout vec4 color;\nvoid main() { color = vec4(1.0); }\n"; + + private GlTestSupport() {} + + public static void drainErrors() { + while (GL11.glGetError() != GL11.GL_NO_ERROR) {} + } + + public static int compile(int type, String source) { + final int shader = GLStateManager.glCreateShader(type); + GLStateManager.glShaderSource(shader, source); + GLStateManager.glCompileShader(shader); + if (GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) == 0) { + fail("test shader failed to compile:\n" + GLStateManager.glGetShaderInfoLog(shader, 4096) + "\n\n" + source); + } + return shader; + } + + /** Links and deletes the two shaders. */ + public static int link(int vs, int fs) { + final int p = GLStateManager.glCreateProgram(); + GLStateManager.glAttachShader(p, vs); + GLStateManager.glAttachShader(p, fs); + GLStateManager.glLinkProgram(p); + assertNotEquals(0, GLStateManager.glGetProgrami(p, GL20.GL_LINK_STATUS), () -> "test program must link: " + GLStateManager.glGetProgramInfoLog(p, 4096)); + GLStateManager.glDeleteShader(vs); + GLStateManager.glDeleteShader(fs); + return p; + } + + public static int program(String vertexSource, String fragmentSource) { + return link(compile(GL20.GL_VERTEX_SHADER, vertexSource), compile(GL20.GL_FRAGMENT_SHADER, fragmentSource)); + } + + /** Raw driver GL_TEXTURE_BINDING_2D of a unit, bypassing the GLSM cache. */ + public static int driverTextureBinding(int unit) { + final int saved = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE); + GL13.glActiveTexture(GL13.GL_TEXTURE0 + unit); + final int binding = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D); + GL13.glActiveTexture(saved); + return binding; + } + + public static FloatBuffer floats(float... values) { + final FloatBuffer buf = BufferUtils.createFloatBuffer(values.length); + buf.put(values).flip(); + return buf; + } + + /** GLSM's answer for a 16-float matrix pname. */ + public static FloatBuffer matrix(int pname) { + final FloatBuffer buf = BufferUtils.createFloatBuffer(16); + GLStateManager.glGetFloat(pname, buf); + return buf; + } + + public static FloatBuffer modelview() { + return matrix(GL11.GL_MODELVIEW_MATRIX); + } + + public static void assertMatrixEquals(FloatBuffer expected, FloatBuffer actual, String message) { + for (int i = 0; i < 16; i++) { + assertEquals(expected.get(i), actual.get(i), 0.001f, message + " (element " + i + ")"); + } + } +} diff --git a/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/LegacyQueryFixture.java b/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/LegacyQueryFixture.java new file mode 100644 index 000000000..aadf94655 --- /dev/null +++ b/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/LegacyQueryFixture.java @@ -0,0 +1,84 @@ +package com.gtnewhorizons.angelica.glsm; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL14; +import org.lwjgl.opengl.GL20; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public final class LegacyQueryFixture { + + public static final int[] CLIENT_ARRAY_CAPS = { + GL11.GL_VERTEX_ARRAY, GL11.GL_NORMAL_ARRAY, GL11.GL_COLOR_ARRAY, GL11.GL_TEXTURE_COORD_ARRAY, + GL11.GL_INDEX_ARRAY, GL11.GL_EDGE_FLAG_ARRAY, GL14.GL_SECONDARY_COLOR_ARRAY, GL14.GL_FOG_COORDINATE_ARRAY + }; + + public static final int[] HINT_TARGETS = { + GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_POINT_SMOOTH_HINT, GL11.GL_LINE_SMOOTH_HINT, GL11.GL_POLYGON_SMOOTH_HINT, + GL11.GL_FOG_HINT, GL13.GL_TEXTURE_COMPRESSION_HINT, GL14.GL_GENERATE_MIPMAP_HINT, GL20.GL_FRAGMENT_SHADER_DERIVATIVE_HINT + }; + + private LegacyQueryFixture() {} + + /** Every pname LegacyQueries answers from the cache. */ + public static int[] legacyPnames() { + final int[] out = new int[0x10000]; + int n = 0; + for (int pname = 0; pname <= 0xFFFF; pname++) { + if (LegacyQueries.desc(pname) != 0) { + out[n++] = pname; + } + } + return Arrays.copyOf(out, n); + } + + public static String hex(int pname) { + return "0x" + Integer.toHexString(pname); + } + + public static void setEnabled(int cap, boolean on) { + if (on) { + GLStateManager.glEnable(cap); + } else { + GLStateManager.glDisable(cap); + } + } + + public static void popAttribTo(int base) { + while (GLStateManager.getAttribDepth() > base) { + GLStateManager.glPopAttrib(); + } + } + + public static void drainClientAttribStack() { + while (GLStateManager.glGetInteger(GL11.GL_CLIENT_ATTRIB_STACK_DEPTH) > 0) { + GLStateManager.glPopClientAttrib(); + } + } + + /** Enables then disables {@code cap}, checking glIsEnabled, glGetBoolean and glGetInteger at each step. */ + public static void assertEnableRoundTrip(int cap, boolean clientState) { + final String name = hex(cap); + if (clientState) { + GLStateManager.glEnableClientState(cap); + } else { + GLStateManager.glEnable(cap); + } + assertTrue(GLStateManager.glIsEnabled(cap), "enabled isEnabled " + name); + assertTrue(GLStateManager.glGetBoolean(cap), "enabled getBoolean " + name); + assertEquals(1, GLStateManager.glGetInteger(cap), "enabled getInteger " + name); + if (clientState) { + GLStateManager.glDisableClientState(cap); + } else { + GLStateManager.glDisable(cap); + } + assertFalse(GLStateManager.glIsEnabled(cap), "disabled isEnabled " + name); + assertFalse(GLStateManager.glGetBoolean(cap), "disabled getBoolean " + name); + assertEquals(0, GLStateManager.glGetInteger(cap), "disabled getInteger " + name); + } +} diff --git a/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/ffp/FfpFixture.java b/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/ffp/FfpFixture.java index 3ed8d0baa..75c9532db 100644 --- a/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/ffp/FfpFixture.java +++ b/glsm/src/testFixtures/java/com/gtnewhorizons/angelica/glsm/ffp/FfpFixture.java @@ -4,20 +4,18 @@ import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat; import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement; import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormatElement.Usage; import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL20; import java.nio.ByteBuffer; import java.util.function.IntFunction; import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.memAddress0; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; public final class FfpFixture { @@ -128,26 +126,7 @@ public final class FfpFixture { } private static int buildProgram() { - final int vs = compile(GL20.GL_VERTEX_SHADER, VS); - final int fs = compile(GL20.GL_FRAGMENT_SHADER, FS); - final int p = GLStateManager.glCreateProgram(); - GLStateManager.glAttachShader(p, vs); - GLStateManager.glAttachShader(p, fs); - GLStateManager.glLinkProgram(p); - assertNotEquals(0, GLStateManager.glGetProgrami(p, GL20.GL_LINK_STATUS), () -> "test program must link: " + GLStateManager.glGetProgramInfoLog(p, 4096)); - GLStateManager.glDeleteShader(vs); - GLStateManager.glDeleteShader(fs); - return p; - } - - private static int compile(int type, String source) { - final int shader = GLStateManager.glCreateShader(type); - GLStateManager.glShaderSource(shader, source); - GLStateManager.glCompileShader(shader); - if (GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) == 0) { - fail("test shader failed to compile:\n" + GLStateManager.glGetShaderInfoLog(shader, 4096) + "\n\n" + source); - } - return shader; + return GlTestSupport.program(VS, FS); } } diff --git a/lwjgl3-backend/src/main/java/com/gtnewhorizons/angelica/lwjgl3/Lwjgl3GLRenderBackend.java b/lwjgl3-backend/src/main/java/com/gtnewhorizons/angelica/lwjgl3/Lwjgl3GLRenderBackend.java index aebf9ef7b..0192596ac 100644 --- a/lwjgl3-backend/src/main/java/com/gtnewhorizons/angelica/lwjgl3/Lwjgl3GLRenderBackend.java +++ b/lwjgl3-backend/src/main/java/com/gtnewhorizons/angelica/lwjgl3/Lwjgl3GLRenderBackend.java @@ -451,6 +451,7 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend { @Override public void hint(int target, int mode) { + if (RenderSystem.isGLES() && target != GL20C.GL_FRAGMENT_SHADER_DERIVATIVE_HINT) return; GL11C.glHint(target, mode); } @@ -1822,11 +1823,13 @@ public final class Lwjgl3GLRenderBackend extends RenderBackend { @Override public void pointParameterf(int pname, float param) { + if (RenderSystem.isGLES()) return; GL14C.glPointParameterf(pname, param); } @Override public void pointParameteri(int pname, int param) { + if (RenderSystem.isGLES()) return; GL14C.glPointParameteri(pname, param); } diff --git a/sdl-gpu/src/main/java/com/gtnewhorizons/angelica/sdlgpu/SDLGPURenderBackend.java b/sdl-gpu/src/main/java/com/gtnewhorizons/angelica/sdlgpu/SDLGPURenderBackend.java index 0472c65c9..40320b58b 100644 --- a/sdl-gpu/src/main/java/com/gtnewhorizons/angelica/sdlgpu/SDLGPURenderBackend.java +++ b/sdl-gpu/src/main/java/com/gtnewhorizons/angelica/sdlgpu/SDLGPURenderBackend.java @@ -4126,7 +4126,7 @@ public class SDLGPURenderBackend extends RenderBackend { } @Override public void getFloat(int pname, FloatBuffer params) { if (params == null || params.remaining() == 0) return; - if (pname == GL12.GL_ALIASED_LINE_WIDTH_RANGE || pname == GL11.GL_LINE_WIDTH_RANGE) { + if (pname == GL12.GL_ALIASED_LINE_WIDTH_RANGE || pname == GL11.GL_LINE_WIDTH_RANGE || pname == GL12.GL_ALIASED_POINT_SIZE_RANGE || pname == GL11.GL_POINT_SIZE_RANGE) { if (params.remaining() >= 2) { params.put(params.position(), 1.0f); params.put(params.position() + 1, 1.0f); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/DrawBuffersLimitTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/DrawBuffersLimitTest.java index 75b1bab4e..83fd55749 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/DrawBuffersLimitTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/DrawBuffersLimitTest.java @@ -1,6 +1,5 @@ package com.gtnewhorizons.angelica.sdlgpu; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; import com.gtnewhorizons.angelica.sdlgpu.resource.FboState; import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager; @@ -31,7 +30,7 @@ class DrawBuffersLimitTest { @BeforeAll static void createBackend() { backend = new SDLGPURenderBackend(); - resourceManager = Reflect.get(backend, "resourceManager"); + resourceManager = SdlTestRig.resourceManagerOf(backend); } @BeforeEach diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/ImageAccessUsageMappingTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/ImageAccessUsageMappingTest.java index ed89374fa..38690eaca 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/ImageAccessUsageMappingTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/ImageAccessUsageMappingTest.java @@ -1,38 +1,29 @@ package com.gtnewhorizons.angelica.sdlgpu; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL15; +import java.util.stream.Stream; + import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE; -import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE; class ImageAccessUsageMappingTest { - @Test - void readOnlyRequestsOnlyStorageRead() { - assertEquals(SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ, SDLGPURenderBackend.imageUsageForAccess(GL15.GL_READ_ONLY)); + /** Never READ|WRITE together: that leaves SDL's DefaultTextureUsageMode ambiguous on a sampled texture. */ + static Stream accessModes() { + return Stream.of( + Arguments.of("GL_READ_ONLY", GL15.GL_READ_ONLY, SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ), + Arguments.of("GL_WRITE_ONLY", GL15.GL_WRITE_ONLY, SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE), + Arguments.of("GL_READ_WRITE", GL15.GL_READ_WRITE, SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE)); } - @Test - void writeOnlyRequestsOnlyStorageWrite() { - assertEquals(SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE, SDLGPURenderBackend.imageUsageForAccess(GL15.GL_WRITE_ONLY)); - } - - @Test - void readWriteRequestsOnlyStorageWrite() { - assertEquals(SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE, SDLGPURenderBackend.imageUsageForAccess(GL15.GL_READ_WRITE)); - } - - @Test - void noAccessModeRequestsBothStorageBits() { - final int both = SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE; - for (int access : new int[] { GL15.GL_READ_ONLY, GL15.GL_WRITE_ONLY, GL15.GL_READ_WRITE }) { - final int usage = SDLGPURenderBackend.imageUsageForAccess(access); - assertEquals(0, usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE); - assertNotEquals(both, usage & both, "READ|WRITE together leaves SDL's DefaultTextureUsageMode ambiguous on a sampled texture"); - } + @ParameterizedTest(name = "{0}") + @MethodSource("accessModes") + void accessModeRequestsExactlyOneStorageBit(String name, int access, int expectedUsage) { + assertEquals(expectedUsage, SDLGPURenderBackend.imageUsageForAccess(access)); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/RealDeviceExtension.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/RealDeviceExtension.java new file mode 100644 index 000000000..4fcb1cb53 --- /dev/null +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/RealDeviceExtension.java @@ -0,0 +1,51 @@ +package com.gtnewhorizons.angelica.sdlgpu; + +import com.gtnewhorizons.angelica.sdlgpu.resource.PixelOps; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL30; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; + +/** Owns one real SDL GPU device per test class. Runs before the class's own {@code @BeforeAll} and after its {@code @AfterAll}. */ +public final class RealDeviceExtension implements BeforeAllCallback, AfterAllCallback { + + private static SdlTestRig rig; + + public record Target(long texture, int sdlFormat) {} + + public static SdlTestRig rig() { + return rig; + } + + @Override + public void beforeAll(ExtensionContext context) throws Exception { + rig = SdlTestRig.acquireRealDevice(); + } + + @Override + public void afterAll(ExtensionContext context) { + if (rig != null && rig.frameManager.isFrameActive()) rig.frameManager.endFrame(); + rig = null; + SdlTestRig.releaseRealDevice(); + } + + public static Target colorTarget(int glId, int width, int height) { + final long texture = rig.resourceManager.createTexture(glId, GL11.GL_TEXTURE_2D, GL11.GL_RGBA8, width, height, 1, 1); + assertTrue(texture != 0, "color target creation failed"); + return new Target(texture, rig.resourceManager.getTextureMeta(glId).sdlFormat()); + } + + public static Target depthStencilTarget(int glId, int width, int height) { + rig.resourceManager.cachePreferredDepthFormats(); + rig.resourceManager.createTexture(glId, GL11.GL_TEXTURE_2D, GL30.GL_DEPTH24_STENCIL8, width, height, 1, 1); + final long texture = rig.resourceManager.ensureTextureUsage(glId, SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET); + assertTrue(texture != 0, "depth+stencil target creation failed"); + final int format = rig.resourceManager.getTextureMeta(glId).sdlFormat(); + assertTrue(PixelOps.isDepthStencilFormat(format), "test needs a packed depth+stencil format"); + return new Target(texture, format); + } +} diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/SDLGPUBufferQueryTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/SDLGPUBufferQueryTest.java index dd63c5bd1..bf399d87f 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/SDLGPUBufferQueryTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/SDLGPUBufferQueryTest.java @@ -6,7 +6,6 @@ import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL30; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; class SDLGPUBufferQueryTest { @@ -28,11 +27,6 @@ class SDLGPUBufferQueryTest { assertEquals(GL11.GL_NONE, SDLGPURenderBackend.bufferEnum(7, -1)); } - @Test - void defaultMatchesGLStateManagerDeclaredDefault() { - assertEquals(0x405, SDLGPURenderBackend.bufferEnum(0, 0)); - } - @Test void boundTextureReadsTheActiveUnitsSlot() { final int[] bound = {11, 22, 33, 44}; @@ -56,12 +50,6 @@ class SDLGPUBufferQueryTest { assertEquals(256, SDLGPURenderBackend.deviceLimit(GL30.GL_MAX_ARRAY_TEXTURE_LAYERS)); } - @Test - void uniformLimitFitsSdlsUboSection() { - assertTrue(SDLGPURenderBackend.deviceLimit(GL20.GL_MAX_VERTEX_UNIFORM_COMPONENTS) * 4 <= 4096); - assertTrue(SDLGPURenderBackend.deviceLimit(GL30.GL_MAX_ARRAY_TEXTURE_LAYERS) <= 256); - } - @Test void nonLimitPnamesAreNotClaimed() { assertEquals(-1, SDLGPURenderBackend.deviceLimit(GL11.GL_DRAW_BUFFER)); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/SdlTestRig.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/SdlTestRig.java index 9e97acc5c..fec15f784 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/SdlTestRig.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/SdlTestRig.java @@ -5,12 +5,11 @@ import com.gtnewhorizons.angelica.sdlgpu.device.Device; import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager; +import com.gtnewhorizons.angelica.sdlgpu.resource.TransferThread; import org.lwjgl.sdl.SDLError; import org.lwjgl.sdl.SDLHints; import org.lwjgl.sdl.SDLInit; -import java.lang.reflect.Field; - import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.lwjgl.sdl.SDLGPU.SDL_CreateGPUDevice; import static org.lwjgl.sdl.SDLGPU.SDL_DestroyGPUDevice; @@ -31,16 +30,12 @@ public final class SdlTestRig { public final ResourceManager resourceManager; public final long sdlHandle; - private SdlTestRig(long sdlHandle) throws ReflectiveOperationException { + private SdlTestRig(long sdlHandle) { this.sdlHandle = sdlHandle; this.device = new Device(); if (sdlHandle != 0L) { - final Field deviceField = Device.class.getDeclaredField("device"); - deviceField.setAccessible(true); - deviceField.setLong(this.device, sdlHandle); - final Field formatsField = Device.class.getDeclaredField("supportedShaderFormats"); - formatsField.setAccessible(true); - formatsField.setInt(this.device, SDL_GetGPUShaderFormats(sdlHandle)); + Reflect.set(this.device, "device", sdlHandle); + Reflect.set(this.device, "supportedShaderFormats", SDL_GetGPUShaderFormats(sdlHandle)); Reflect.set(this.device, "driverName", SDL_GetGPUDeviceDriver(sdlHandle)); Reflect.invoke(this.device, "probeFences", new Class[] { int.class }, SDL_GetVersion()); } @@ -49,11 +44,7 @@ public final class SdlTestRig { } public static SdlTestRig create() { - try { - return new SdlTestRig(0L); - } catch (ReflectiveOperationException e) { - throw new AssertionError(e); - } + return new SdlTestRig(0L); } public static ResourceManager resourceManager() { @@ -68,6 +59,31 @@ public final class SdlTestRig { return Reflect.invokeStatic(SDLGPURenderBackend.class, "s", new Class[0]); } + public static ResourceManager resourceManagerOf(SDLGPURenderBackend backend) { + return Reflect.get(backend, "resourceManager"); + } + + /** Not installed on the resource manager; pair with {@link #shutdown(TransferThread)}. */ + public TransferThread newTransferThread() { + return new TransferThread(device, resourceManager); + } + + public TransferThread installTransferThread() { + final TransferThread tt = newTransferThread(); + resourceManager.setTransferThread(tt); + return tt; + } + + public static void shutdown(TransferThread tt) { + tt.shutdown(); + try { + tt.getThread().join(5_000L); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new AssertionError(e); + } + } + private static long realSdlDevice; private static final int SHADER_FORMATS = SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_MSL | SDL_GPU_SHADERFORMAT_DXBC | SDL_GPU_SHADERFORMAT_DXIL; @@ -77,7 +93,7 @@ public final class SdlTestRig { assumeTrue(SDLInit.SDL_Init(SDL_INIT_VIDEO), () -> "SDL_Init failed: " + SDLError.SDL_GetError()); } - public static SdlTestRig acquireRealDevice() throws ReflectiveOperationException { + public static SdlTestRig acquireRealDevice() { initSdlVideo(); realSdlDevice = SDL_CreateGPUDevice(SHADER_FORMATS, true, (CharSequence) null); assumeTrue(realSdlDevice != 0, "No SDL GPU device: " + SDLError.SDL_GetError()); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/UboShadowSubDataTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/UboShadowSubDataTest.java index e7180fc88..e312385b9 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/UboShadowSubDataTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/UboShadowSubDataTest.java @@ -9,7 +9,6 @@ import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL31; import org.lwjgl.system.MemoryUtil; -import java.lang.reflect.Field; import java.nio.ByteBuffer; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -20,11 +19,9 @@ class UboShadowSubDataTest { private static ResourceManager resourceManager; @BeforeAll - static void createBackend() throws Exception { + static void createBackend() { backend = new SDLGPURenderBackend(); - final Field f = SDLGPURenderBackend.class.getDeclaredField("resourceManager"); - f.setAccessible(true); - resourceManager = (ResourceManager) f.get(backend); + resourceManager = SdlTestRig.resourceManagerOf(backend); } @ParameterizedTest diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/UniformNegativeLocationTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/UniformNegativeLocationTest.java index de26450bf..c6ea8560f 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/UniformNegativeLocationTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/UniformNegativeLocationTest.java @@ -1,5 +1,6 @@ package com.gtnewhorizons.angelica.sdlgpu; +import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineApplier; import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager; @@ -9,7 +10,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.lwjgl.system.MemoryUtil; -import java.lang.reflect.Field; import java.nio.FloatBuffer; import java.nio.IntBuffer; @@ -48,11 +48,8 @@ class UniformNegativeLocationTest { st.releaseUniformStaging(prog); } - private static void clearApplier(SDLGPURenderBackend backend) throws Exception { - final Field f = SDLGPURenderBackend.class.getDeclaredField("pipelineApplier"); - f.setAccessible(true); - f.set(backend, null); - assertNull(f.get(backend), "test rig failed to null the applier"); + private static void clearApplier(SDLGPURenderBackend backend) { + Reflect.set(backend, "pipelineApplier", null); } @Test diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/compat/Lwjgl3GLCapabilitiesShimTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/compat/Lwjgl3GLCapabilitiesShimTest.java index 056b5de68..2ab103796 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/compat/Lwjgl3GLCapabilitiesShimTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/compat/Lwjgl3GLCapabilitiesShimTest.java @@ -1,5 +1,6 @@ package com.gtnewhorizons.angelica.sdlgpu.compat; +import com.gtnewhorizons.angelica.glsm.testutil.TestThreads; import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend; import org.junit.jupiter.api.Test; import org.lwjgl.opengl.GL; @@ -9,11 +10,9 @@ import java.lang.reflect.Field; import java.util.HashSet; import java.util.Set; import java.util.TreeSet; -import java.util.concurrent.atomic.AtomicReference; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -31,18 +30,6 @@ class Lwjgl3GLCapabilitiesShimTest { assertTrue(caps.forwardCompatible, "SDL reports Core Profile Equivalent, so forwardCompatible must be true"); } - @Test - void everyAdvertisedVersionFlagIsTrue() throws ReflectiveOperationException { - final Set advertised = SDLGPURenderBackend.advertisedCapabilities(); - final GLCapabilities caps = Lwjgl3GLCapabilitiesShim.create(advertised); - - final Set missing = new TreeSet<>(); - for (String name : advertised) { - if (name.startsWith("OpenGL") && !flag(caps, name)) missing.add(name); - } - assertTrue(missing.isEmpty(), () -> "advertised but reported unsupported: " + missing); - } - @Test void advertises46() throws ReflectiveOperationException { final GLCapabilities caps = Lwjgl3GLCapabilitiesShim.create(SDLGPURenderBackend.advertisedCapabilities()); @@ -92,17 +79,8 @@ class Lwjgl3GLCapabilitiesShimTest { try { assertNotNull(GL.getCapabilities()); - final AtomicReference onOtherThread = new AtomicReference<>(); - final Thread other = new Thread(() -> { - try { - GL.getCapabilities(); - } catch (Throwable t) { - onOtherThread.set(t); - } - }, "no-context-thread"); - other.start(); - other.join(); - assertInstanceOf(IllegalStateException.class, onOtherThread.get(), "a thread that does not own the context must not look like the render thread"); + TestThreads.run("no-context-thread", () -> assertThrows(IllegalStateException.class, GL::getCapabilities, + "a thread that does not own the context must not look like the render thread")); } finally { Lwjgl3GLCapabilitiesShim.clearOnCurrentThread(); } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/compute/ChunkCullSpirvCompileTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/compute/ChunkCullSpirvCompileTest.java index 6d1d37a7f..0f9cbc1be 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/compute/ChunkCullSpirvCompileTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/compute/ChunkCullSpirvCompileTest.java @@ -1,6 +1,5 @@ package com.gtnewhorizons.angelica.sdlgpu.compute; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import com.gtnewhorizons.angelica.glsm.testutil.TestPaths; import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager; import org.junit.jupiter.api.Test; @@ -11,9 +10,9 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.stream.IntStream; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; class ChunkCullSpirvCompileTest { @@ -25,9 +24,7 @@ class ChunkCullSpirvCompileTest { @Test void chunkCullReflectsExpectedBindings() { - final SpirvCompiler.Result r = SpirvCompiler.compile(source(), Shaderc.shaderc_compute_shader, SHADER, SpirvCompiler.Options.vulkanForced460Core()); - if (r.spirv() == null) fail(SHADER + " failed to compile: " + r.error()); - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(source(), Shaderc.shaderc_compute_shader); try { final ShaderManager.ComputeBindingMap map = ShaderManager.remapSpirvForComputeSDLGPU(spirv); assertEquals(0, map.samplerGlSlots().length, "the frustum cull samples no textures"); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/compute/ImageAtomicsGpuTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/compute/ImageAtomicsGpuTest.java deleted file mode 100644 index d2a59da5a..000000000 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/compute/ImageAtomicsGpuTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.gtnewhorizons.angelica.sdlgpu.compute; - -import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeTrue; -import static org.lwjgl.sdl.SDLGPU.SDL_GetGPUDeviceDriver; - -class ImageAtomicsGpuTest { - - private static SdlTestRig rig; - private static ImageAtomicsProbe probe; - - @BeforeAll - static void setUp() throws Exception { - rig = SdlTestRig.acquireRealDevice(); - assumeTrue("metal".equalsIgnoreCase(SDL_GetGPUDeviceDriver(rig.sdlHandle)), "native Metal only"); - assumeTrue(ImageAtomicsProbe.defaultMslVersion() >= ImageAtomicsProbe.MTL_LANGUAGE_VERSION_3_1, "this process compiles Metal source below MSL 3.1"); - probe = new ImageAtomicsProbe(rig.sdlHandle); - } - - @AfterAll - static void tearDown() { - if (probe != null) probe.release(); - SdlTestRig.releaseRealDevice(); - } - - @Test - void atomicsCountEveryInvocationOnAnImageTheSizeOfEuphoriasEndCrystalMap() { - assertTrue(probe.run(40, 10)); - } -} diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/device/FenceProbeRealDeviceTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/device/FenceProbeRealDeviceTest.java index f038c4733..2865f93d8 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/device/FenceProbeRealDeviceTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/device/FenceProbeRealDeviceTest.java @@ -1,9 +1,9 @@ package com.gtnewhorizons.angelica.sdlgpu.device; +import com.gtnewhorizons.angelica.sdlgpu.RealDeviceExtension; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.lwjgl.system.MemoryStack; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -17,22 +17,12 @@ import static org.lwjgl.sdl.SDLGPU.SDL_WaitForGPUFences; import static org.lwjgl.sdl.SDLVersion.SDL_GetVersion; import static org.lwjgl.sdl.SDLVersion.SDL_VERSIONNUM; +@ExtendWith(RealDeviceExtension.class) class FenceProbeRealDeviceTest { - private static SdlTestRig rig; - - @BeforeAll - static void setUp() throws Exception { - rig = SdlTestRig.acquireRealDevice(); - } - - @AfterAll - static void tearDown() { - SdlTestRig.releaseRealDevice(); - } - @Test void probeMatchesVersionTableAndCorrectedQueryReadsSignaled() { + final SdlTestRig rig = RealDeviceExtension.rig(); final String driver = SDL_GetGPUDeviceDriver(rig.sdlHandle); assertEquals("metal".equalsIgnoreCase(driver) && SDL_GetVersion() == SDL_VERSIONNUM(3, 4, 14), rig.device.isFenceQueryInverted(), "driver=" + driver + " sdl=" + SDL_GetVersion()); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/device/VideoDriverRecoveryTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/device/VideoDriverRecoveryTest.java index 53d02acb2..bc6a49e19 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/device/VideoDriverRecoveryTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/device/VideoDriverRecoveryTest.java @@ -1,34 +1,31 @@ package com.gtnewhorizons.angelica.sdlgpu.device; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; import static com.gtnewhorizons.angelica.sdlgpu.device.VideoDriverRecovery.shouldRetryOnX11; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; class VideoDriverRecoveryTest { - @Test - void waylandWithoutVulkanSurfaceRetries() { - assertTrue(shouldRetryOnX11("wayland", false, ":0")); + static Stream sessions() { + return Stream.of( + Arguments.of("wayland without a Vulkan loader, XWayland present", "wayland", false, ":0", true), + Arguments.of("wayland with a Vulkan loader", "wayland", true, ":0", false), + Arguments.of("wayland, DISPLAY unset", "wayland", false, null, false), + Arguments.of("wayland, DISPLAY empty", "wayland", false, "", false), + Arguments.of("x11", "x11", false, ":0", false), + Arguments.of("cocoa", "cocoa", false, ":0", false), + Arguments.of("windows", "windows", false, ":0", false), + Arguments.of("no video driver", null, false, ":0", false)); } - @Test - void waylandWithVulkanStays() { - assertFalse(shouldRetryOnX11("wayland", true, ":0")); - } - - @Test - void noXWaylandMeansNoRetry() { - assertFalse(shouldRetryOnX11("wayland", false, null)); - assertFalse(shouldRetryOnX11("wayland", false, "")); - } - - @Test - void otherVideoDriversAreLeftAlone() { - assertFalse(shouldRetryOnX11("x11", false, ":0")); - assertFalse(shouldRetryOnX11("cocoa", false, ":0")); - assertFalse(shouldRetryOnX11("windows", false, ":0")); - assertFalse(shouldRetryOnX11(null, false, ":0")); + @ParameterizedTest(name = "{0}") + @MethodSource("sessions") + void retriesOnX11OnlyForWaylandWithoutVulkan(String name, String videoDriver, boolean vulkanLoaderAvailable, String display, boolean retry) { + assertEquals(retry, shouldRetryOnX11(videoDriver, vulkanLoaderAvailable, display)); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/frame/AcquireAffinityTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/frame/AcquireAffinityTest.java index 9ebeb87cf..347795ab7 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/frame/AcquireAffinityTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/frame/AcquireAffinityTest.java @@ -1,28 +1,29 @@ package com.gtnewhorizons.angelica.sdlgpu.frame; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; class AcquireAffinityTest { private static final Thread WINDOW = new Thread("window"); private static final Thread OTHER = new Thread("other"); - @Test - void acquiringOnTheWindowThreadIsTheOnlyAcceptedCase() { - assertFalse(FrameManager.acquireAffinityViolated(WINDOW, WINDOW)); + /** A null window is never a violation: the check runs before the window is claimed. */ + static Stream acquires() { + return Stream.of( + Arguments.of("on the window thread", WINDOW, WINDOW, false), + Arguments.of("on any other thread", OTHER, WINDOW, true), + Arguments.of("before the window thread is known", OTHER, null, false)); } - @Test - void acquiringOnAnyOtherThreadViolatesTheSdlContract() { - assertTrue(FrameManager.acquireAffinityViolated(OTHER, WINDOW)); - } - - @Test - void anUnknownWindowThreadCannotBeJudged() { - assertFalse(FrameManager.acquireAffinityViolated(OTHER, null), - "the check runs before the window is claimed; judging a null window would throw at startup"); + @ParameterizedTest(name = "{0}") + @MethodSource("acquires") + void acquireAffinityViolated(String name, Thread self, Thread window, boolean violated) { + assertEquals(violated, FrameManager.acquireAffinityViolated(self, window)); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/frame/PresenterTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/frame/PresenterTest.java index 3b604b77f..839051945 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/frame/PresenterTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/frame/PresenterTest.java @@ -1,6 +1,7 @@ package com.gtnewhorizons.angelica.sdlgpu.frame; import com.gtnewhorizons.angelica.glsm.backend.MainThreadPump; +import com.gtnewhorizons.angelica.glsm.testutil.TestThreads; import com.gtnewhorizons.angelica.sdlgpu.device.Device; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -115,9 +116,7 @@ class PresenterTest { p.requestPresent(1L, 10, 10, SDL_FLIP_NONE); - final Thread windowThread = new Thread(executor::runNext, "window-thread"); - windowThread.start(); - windowThread.join(); + TestThreads.run("window-thread", executor::runNext); assertEquals(before, fm.registeredFrameCount(), "the window thread must stay out of the frame registry"); } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlAttachmentClearTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlAttachmentClearTest.java index 6b58516bd..59ac00748 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlAttachmentClearTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlAttachmentClearTest.java @@ -2,11 +2,8 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.config.SystemProperties; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import com.gtnewhorizons.angelica.glsm.backend.BackendManager; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -21,6 +18,8 @@ import java.nio.FloatBuffer; import java.nio.IntBuffer; import sun.misc.Unsafe; +import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.BLACK; +import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.RED; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -30,22 +29,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class GlsmSdlAttachmentClearTest { private static final int SIZE = GlsmSdlHeadlessRig.SIZE; - private static final int RED = 0xFFFF0000; - private static final int BLACK = 0xFF000000; - - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - - private static FrameManager frameManager() { - return Reflect.get(BackendManager.RENDER_BACKEND, "frameManager"); - } private static FrameManager.FrameState frame() { - return frameManager().frame(); + return GlsmSdlHeadlessRig.frameManager().frame(); } + // Reflect.setStaticFinal rejects primitive fields. private static void setDisableInPassClear(boolean value) { try { final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); @@ -58,6 +47,27 @@ class GlsmSdlAttachmentClearTest { } } + /** Clears colour to black and depth to 0.9, then draws a quad so a render pass is open. */ + private static void primeTarget(float r, float g, float b) { + GlsmSdlHeadlessRig.bindTarget(); + GLStateManager.glDepthMask(true); + GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + GLStateManager.glClearDepth(0.9); + GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); + GlsmSdlHeadlessRig.solidQuad(r, g, b); + } + + private static void primeTargetWithStencil(int stencil) { + GlsmSdlHeadlessRig.bindTarget(); + GLStateManager.glDepthMask(true); + GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + GLStateManager.glClearDepth(1.0); + GLStateManager.glClearStencil(stencil); + GLStateManager.glStencilMask(0xFF); + GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT); + GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f); + } + private static void drawFullQuadAtZ(float z, float r, float g, float b) { GLStateManager.glColor4f(r, g, b, 1.0f); GLStateManager.glNormal3f(0.0f, 0.0f, 1.0f); @@ -93,12 +103,7 @@ class GlsmSdlAttachmentClearTest { @Test void depthMaskOffSuppressesTheClearAndBreaksNoPass() { - GlsmSdlHeadlessRig.bindTarget(); - GLStateManager.glDepthMask(true); - GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - GLStateManager.glClearDepth(0.9); - GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); - GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f); + primeTarget(0.1f, 0.1f, 0.1f); final FrameManager.FrameState f = frame(); final long passBefore = f.renderPass; @@ -119,12 +124,7 @@ class GlsmSdlAttachmentClearTest { @Test void inPassPathIsTakenForACompatibleActivePass() { - GlsmSdlHeadlessRig.bindTarget(); - GLStateManager.glDepthMask(true); - GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - GLStateManager.glClearDepth(0.9); - GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); - GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f); + primeTarget(0.1f, 0.1f, 0.1f); final FrameManager.FrameState f = frame(); final long passBefore = f.renderPass; @@ -142,12 +142,7 @@ class GlsmSdlAttachmentClearTest { @Test void disableInPassClearFlagForcesTheLegacyPath() { - GlsmSdlHeadlessRig.bindTarget(); - GLStateManager.glDepthMask(true); - GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - GLStateManager.glClearDepth(0.9); - GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); - GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f); + primeTarget(0.1f, 0.1f, 0.1f); final FrameManager.FrameState f = frame(); assertTrue(f.renderPass != 0, "expected an active render pass before the clear"); @@ -177,12 +172,7 @@ class GlsmSdlAttachmentClearTest { try { GlsmSdlHeadlessRig.beginFrame(); GlsmSdlHeadlessRig.resetState(); - GlsmSdlHeadlessRig.bindTarget(); - GLStateManager.glDepthMask(true); - GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - GLStateManager.glClearDepth(0.9); - GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); - GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f); + primeTarget(0.1f, 0.1f, 0.1f); GLStateManager.glDepthMask(true); GLStateManager.glClearDepth(depthValue); @@ -196,12 +186,7 @@ class GlsmSdlAttachmentClearTest { @Test void scissoredClearOnlyAffectsTheScissorRect() { - GlsmSdlHeadlessRig.bindTarget(); - GLStateManager.glDepthMask(true); - GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - GLStateManager.glClearDepth(0.9); - GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); - GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f); + primeTarget(0.1f, 0.1f, 0.1f); GLStateManager.glEnable(GL11.GL_SCISSOR_TEST); GLStateManager.glScissor(0, 0, SIZE / 2, SIZE); @@ -229,14 +214,7 @@ class GlsmSdlAttachmentClearTest { @Test void stencilWriteMaskIsHonored() { - GlsmSdlHeadlessRig.bindTarget(); - GLStateManager.glDepthMask(true); - GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - GLStateManager.glClearDepth(1.0); - GLStateManager.glClearStencil(0xA5); - GLStateManager.glStencilMask(0xFF); - GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT); - GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f); + primeTargetWithStencil(0xA5); final FrameManager.FrameState f = frame(); final long passBefore = f.renderPass; @@ -258,14 +236,7 @@ class GlsmSdlAttachmentClearTest { @Test @Disabled("stencil reference >= 0x80 compares as 0xFF on this backend; see stencil reference finding") void stencilReferenceAtOrAbove0x80ComparesExactly() { - GlsmSdlHeadlessRig.bindTarget(); - GLStateManager.glDepthMask(true); - GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - GLStateManager.glClearDepth(1.0); - GLStateManager.glClearStencil(0xAF); - GLStateManager.glStencilMask(0xFF); - GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT); - GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f); + primeTargetWithStencil(0xAF); final StencilProbeResult result = probeStencilReconcile(); assertEquals(0xAF, result.bitPlaneValue(), "bit-plane read of a plain 0xAF stencil clear"); @@ -274,14 +245,7 @@ class GlsmSdlAttachmentClearTest { @Test void baselineStencilClearReadsBackExactly() { - GlsmSdlHeadlessRig.bindTarget(); - GLStateManager.glDepthMask(true); - GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - GLStateManager.glClearDepth(1.0); - GLStateManager.glClearStencil(0xA5); - GLStateManager.glStencilMask(0xFF); - GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT); - GlsmSdlHeadlessRig.solidQuad(0.1f, 0.1f, 0.1f); + primeTargetWithStencil(0xA5); assertEquals(0xA5, readStencilByteSameFrame(), "a full-write-mask clear (the legacy load-op path) must read back exactly what was cleared"); } @@ -375,12 +339,7 @@ class GlsmSdlAttachmentClearTest { @Test void scissoredColorAndDepthClearStaysInTheRect() { - GlsmSdlHeadlessRig.bindTarget(); - GLStateManager.glDepthMask(true); - GLStateManager.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - GLStateManager.glClearDepth(0.9); - GLStateManager.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); - GlsmSdlHeadlessRig.solidQuad(0.0f, 0.0f, 1.0f); + primeTarget(0.0f, 0.0f, 1.0f); final FrameManager.FrameState f = frame(); final long passBefore = f.renderPass; @@ -594,7 +553,7 @@ class GlsmSdlAttachmentClearTest { GLStateManager.glDepthMask(true); GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT); - final ResourceManager resourceManager = Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager"); + final ResourceManager resourceManager = GlsmSdlHeadlessRig.resourceManager(); final long depthTexture = resourceManager.getFbo(fbo).depthTexture; GlsmSdlHeadlessRig.endFrame(); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlBlendConstantsTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlBlendConstantsTest.java index 70415c3a3..75d4c507b 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlBlendConstantsTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlBlendConstantsTest.java @@ -1,14 +1,12 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import com.gtnewhorizons.angelica.glsm.backend.BackendManager; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL14; @@ -19,11 +17,6 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; @ExtendWith(GlsmSdlFrameExtension.class) class GlsmSdlBlendConstantsTest { - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - @AfterEach void restoreBlend() { GLStateManager.glDisable(GL11.GL_BLEND); @@ -31,17 +24,10 @@ class GlsmSdlBlendConstantsTest { GLStateManager.glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); } - @Test - void pendingConstantsSurviveADrawWithoutConstantBlending() { - verifyConstants(false); - } - - @Test - void constantsChangedWhileBlendingIsDisabledReachTheNextBlendedDraw() { - verifyConstants(true); - } - - private static void verifyConstants(boolean previouslyEnabled) { + /** With blending never enabled before, the pending constants must survive a draw that does not use them. */ + @ParameterizedTest(name = "blending enabled earlier in the frame: {0}") + @ValueSource(booleans = {false, true}) + void constantsSetWhileBlendingIsDisabledReachTheNextBlendedDraw(boolean previouslyEnabled) { GlsmSdlHeadlessRig.bindTarget(); GLStateManager.glBlendFunc(GL14.GL_CONSTANT_COLOR, GL11.GL_ZERO); if (previouslyEnabled) { @@ -52,8 +38,7 @@ class GlsmSdlBlendConstantsTest { GLStateManager.glDisable(GL11.GL_BLEND); GLStateManager.glBlendColor(0.0f, 1.0f, 0.0f, 1.0f); GlsmSdlHeadlessRig.solidQuad(1.0f, 1.0f, 1.0f); - final FrameManager manager = Reflect.get(BackendManager.RENDER_BACKEND, "frameManager"); - final FrameManager.FrameState frame = manager.frame(); + final FrameManager.FrameState frame = GlsmSdlHeadlessRig.frameManager().frame(); final long pass = frame.renderPass; final long generation = frame.renderPassGeneration; assertNotEquals(0, pass); @@ -64,6 +49,6 @@ class GlsmSdlBlendConstantsTest { assertEquals(generation, frame.renderPassGeneration); final int[] pixels = GlsmSdlHeadlessRig.readTarget(); final int size = GlsmSdlHeadlessRig.SIZE; - assertEquals(0xFF00FF00, GlsmSdlHeadlessRig.pixelAt(pixels, size, size / 2, size / 2)); + assertEquals(GlsmSdlHeadlessRig.GREEN, GlsmSdlHeadlessRig.pixelAt(pixels, size, size / 2, size / 2)); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlBufferMappingLifetimeTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlBufferMappingLifetimeTest.java index 00ad19cc7..00dc2179e 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlBufferMappingLifetimeTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlBufferMappingLifetimeTest.java @@ -1,10 +1,12 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL30; @@ -14,6 +16,7 @@ import org.lwjgl.opengl.GL44; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -31,11 +34,6 @@ class GlsmSdlBufferMappingLifetimeTest { private static final int PERSISTENT_MAP = GL44.GL_MAP_PERSISTENT_BIT | GL30.GL_MAP_WRITE_BIT | GL44.GL_MAP_COHERENT_BIT; private static final int PLAIN_MAP = GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_BUFFER_BIT; - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - private static void fill(ByteBuffer buf, int seed) { for (int i = 0; i < buf.capacity(); i++) buf.put(i, (byte) (seed * 37 + i * 7 + 1)); } @@ -300,7 +298,15 @@ class GlsmSdlBufferMappingLifetimeTest { } } - private static void assertBufferDataUnmapsPlainMapping(int target) { + static Stream plainMapTargets() { + return Stream.of( + Arguments.of("GL_ARRAY_BUFFER", GL15.GL_ARRAY_BUFFER), + Arguments.of("GL_UNIFORM_BUFFER", GL31.GL_UNIFORM_BUFFER)); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("plainMapTargets") + void bufferDataOnMappedBufferUnmapsIt(String name, int target) { final int id = GLStateManager.glGenBuffers(); try { GLStateManager.glBindBuffer(target, id); @@ -314,16 +320,6 @@ class GlsmSdlBufferMappingLifetimeTest { } } - @Test - void bufferDataOnMappedArrayBufferUnmapsIt() { - assertBufferDataUnmapsPlainMapping(GL15.GL_ARRAY_BUFFER); - } - - @Test - void bufferDataOnMappedUniformBufferUnmapsIt() { - assertBufferDataUnmapsPlainMapping(GL31.GL_UNIFORM_BUFFER); - } - @Test void outOfRangeFlushOfPersistentMappingIsIgnored() { final int target = GL15.GL_ARRAY_BUFFER; diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlCopyBlitTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlCopyBlitTest.java index cb5de9be1..747085145 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlCopyBlitTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlCopyBlitTest.java @@ -1,16 +1,14 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import com.gtnewhorizons.angelica.glsm.backend.BackendManager; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; -import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate; -import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; -import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL13; @@ -20,39 +18,30 @@ import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; import java.nio.IntBuffer; +import java.util.function.IntBinaryOperator; +import java.util.stream.Stream; +import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.BLUE; +import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.GREEN; +import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.RED; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.SIZE; -import static org.junit.jupiter.api.Assertions.assertEquals; @Tag("glsm-sdl") @ExtendWith(GlsmSdlFrameExtension.class) class GlsmSdlCopyBlitTest { - private static final int RED = 0xFFFF0000; - private static final int GREEN = 0xFF00FF00; - private static final int BLUE = 0xFF0000FF; - private static final int SCISSOR_ROWS = 16; private static final byte SENTINEL_BYTE = 0x7B; private static final int SENTINEL = 0x7B7B7B7B; - private static FrameManager frameManager; - private static ResourceManager resourceManager; - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - frameManager = Reflect.get(BackendManager.RENDER_BACKEND, "frameManager"); - resourceManager = Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager"); - frameManager.destroyFinalTarget(); - frameManager.finalTarget().create(SDLGPUGate.device(), resourceManager, SIZE, SIZE, resourceManager.mapTextureFormat(GL11.GL_RGBA8)); - assertEquals(SIZE, frameManager.getFbo0Height()); + static void installFinalTarget() { + GlsmSdlHeadlessRig.installFinalTarget(SIZE, SIZE); } @AfterAll static void dropFinalTarget() { - GlsmSdlHeadlessRig.endFrame(); - if (frameManager != null) frameManager.destroyFinalTarget(); + GlsmSdlHeadlessRig.dropFinalTarget(); } private static void assertRow(int[] pixels, int y, int expected, String label) { @@ -88,27 +77,11 @@ class GlsmSdlCopyBlitTest { GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MIN_LOD, 0.0f); GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LOD, 1.0f); GLStateManager.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, 0.0f); - uploadLevel(0, SIZE, level0Argb); - uploadLevel(1, SIZE / 2, level1Argb); + GlsmSdlHeadlessRig.uploadSolidLevel(0, SIZE, level0Argb); + GlsmSdlHeadlessRig.uploadSolidLevel(1, SIZE / 2, level1Argb); return id; } - private static void uploadLevel(int level, int size, int argb) { - final ByteBuffer texels = MemoryUtil.memAlloc(size * size * 4); - try { - for (int i = 0; i < size * size; i++) { - texels.put(i * 4, (byte) ((argb >> 16) & 0xFF)); - texels.put(i * 4 + 1, (byte) ((argb >> 8) & 0xFF)); - texels.put(i * 4 + 2, (byte) (argb & 0xFF)); - texels.put(i * 4 + 3, (byte) ((argb >>> 24) & 0xFF)); - } - GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, level, GL11.GL_RGBA8, size, size, 0, - GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels); - } finally { - MemoryUtil.memFree(texels); - } - } - private static int copyGreenIntoLevelOne() { GlsmSdlHeadlessRig.bindTarget(); GlsmSdlHeadlessRig.clearTo(0.0f, 0.0f, 0.0f, 1.0f); @@ -181,7 +154,7 @@ class GlsmSdlCopyBlitTest { private static void blitLevelOneInto(int dstSize, int dstLevel) { final int source = GlsmSdlHeadlessRig.createSolidMipTexture(SIZE, 1, RED); - uploadLevel(1, SIZE / 2, GREEN); + GlsmSdlHeadlessRig.uploadSolidLevel(1, SIZE / 2, GREEN); final int destination = GlsmSdlHeadlessRig.createSolidMipTexture(dstSize, dstLevel, BLUE); final int readFbo = GlsmSdlHeadlessRig.fboWithColor(source, 1); final int drawFbo = GlsmSdlHeadlessRig.fboWithColor(destination, dstLevel); @@ -202,19 +175,17 @@ class GlsmSdlCopyBlitTest { GLStateManager.glDeleteTextures(destination); } - @Test - void blitFramebufferReadsTheSourceAttachmentLevel() { - blitLevelOneInto(SIZE / 2, 0); + static Stream levelBlits() { + return Stream.of( + Arguments.of("1:1 into level 0: reads the source attachment level", SIZE / 2, 0), + Arguments.of("scaled into level 0: reads the source attachment level", SIZE, 0), + Arguments.of("into level 1: writes the destination attachment level", SIZE, 1)); } - @Test - void scaledBlitFramebufferReadsTheSourceAttachmentLevel() { - blitLevelOneInto(SIZE, 0); - } - - @Test - void blitFramebufferWritesTheDestinationAttachmentLevel() { - blitLevelOneInto(SIZE, 1); + @ParameterizedTest(name = "{0}") + @MethodSource("levelBlits") + void blitFramebufferHonoursAttachmentLevels(String name, int dstSize, int dstLevel) { + blitLevelOneInto(dstSize, dstLevel); } @Test @@ -299,12 +270,7 @@ class GlsmSdlCopyBlitTest { try { for (int y = 0; y < SIZE; y++) { for (int x = 0; x < SIZE; x++) { - final int argb = pattern(x, y); - final int i = (y * SIZE + x) * 4; - texels.put(i, (byte) ((argb >> 16) & 0xFF)); - texels.put(i + 1, (byte) ((argb >> 8) & 0xFF)); - texels.put(i + 2, (byte) (argb & 0xFF)); - texels.put(i + 3, (byte) ((argb >>> 24) & 0xFF)); + GlsmSdlHeadlessRig.putArgb(texels, y * SIZE + x, pattern(x, y)); } } GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, SIZE, SIZE, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels); @@ -331,32 +297,25 @@ class GlsmSdlCopyBlitTest { return pixels; } - @Test - void blitFramebufferWithReversedSourceXMirrorsHorizontally() { - final int[] pixels = blitPattern(SIZE, 0, 0, SIZE, 0, 0, SIZE, SIZE); - for (int y = 0; y < SIZE; y++) { - for (int x = 0; x < SIZE; x++) { - GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, x, y, pattern(SIZE - 1 - x, y), "horizontal mirror"); - } - } + private static Arguments mirror(String name, int[] srcRect, int[] dstRect, IntBinaryOperator expected) { + return Arguments.of(name, srcRect, dstRect, expected); } - @Test - void blitFramebufferWithReversedDestinationYMirrorsVertically() { - final int[] pixels = blitPattern(0, 0, SIZE, SIZE, 0, SIZE, SIZE, 0); - for (int y = 0; y < SIZE; y++) { - for (int x = 0; x < SIZE; x++) { - GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, x, y, pattern(x, SIZE - 1 - y), "vertical mirror"); - } - } + /** Rects are x0, y0, x1, y1; {@code expected} maps a destination pixel to the pattern colour it must hold. */ + static Stream mirroredBlits() { + return Stream.of( + mirror("reversed source X mirrors horizontally", new int[]{SIZE, 0, 0, SIZE}, new int[]{0, 0, SIZE, SIZE}, (x, y) -> pattern(SIZE - 1 - x, y)), + mirror("reversed destination Y mirrors vertically", new int[]{0, 0, SIZE, SIZE}, new int[]{0, SIZE, SIZE, 0}, (x, y) -> pattern(x, SIZE - 1 - y)), + mirror("both ranges reversed does not mirror", new int[]{SIZE, SIZE, 0, 0}, new int[]{SIZE, SIZE, 0, 0}, (x, y) -> pattern(x, y))); } - @Test - void blitFramebufferWithBothRangesReversedDoesNotMirror() { - final int[] pixels = blitPattern(SIZE, SIZE, 0, 0, SIZE, SIZE, 0, 0); + @ParameterizedTest(name = "{0}") + @MethodSource("mirroredBlits") + void blitFramebufferMirrorsByRangeDirection(String name, int[] src, int[] dst, IntBinaryOperator expected) { + final int[] pixels = blitPattern(src[0], src[1], src[2], src[3], dst[0], dst[1], dst[2], dst[3]); for (int y = 0; y < SIZE; y++) { for (int x = 0; x < SIZE; x++) { - GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, x, y, pattern(x, y), "both ranges reversed"); + GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, x, y, expected.applyAsInt(x, y), name); } } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlDegenerateDrawTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlDegenerateDrawTest.java index 733ffde64..a5691742c 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlDegenerateDrawTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlDegenerateDrawTest.java @@ -1,34 +1,23 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.lwjgl.opengl.GL11; +import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.RED; + @Tag("glsm-sdl") @ExtendWith(GlsmSdlFrameExtension.class) class GlsmSdlDegenerateDrawTest { - private static final int RED = 0xFFFF0000; - - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - - @Test - void zeroCountDrawArraysLeavesTheFollowingDrawIntact() { - GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, 0); + @ParameterizedTest(name = "count {0}") + @ValueSource(ints = {0, -3}) + void degenerateCountDrawArraysLeavesTheFollowingDrawIntact(int count) { + GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, count); GlsmSdlHeadlessRig.solidQuad(1.0f, 0.0f, 0.0f); - GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), RED, "after a zero-count drawArrays"); - } - - @Test - void negativeCountDrawArraysLeavesTheFollowingDrawIntact() { - GLStateManager.glDrawArrays(GL11.GL_TRIANGLES, 0, -3); - GlsmSdlHeadlessRig.solidQuad(1.0f, 0.0f, 0.0f); - GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), RED, "after a negative-count drawArrays"); + GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), RED, "after a drawArrays with count " + count); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlFbo0ViewportTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlFbo0ViewportTest.java index 0d50ef56f..3853fb078 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlFbo0ViewportTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlFbo0ViewportTest.java @@ -1,10 +1,6 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import com.gtnewhorizons.angelica.glsm.backend.BackendManager; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; -import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate; -import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -19,6 +15,9 @@ import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; +import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.BLUE; +import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.GREEN; +import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.RED; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.SIZE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -32,27 +31,14 @@ class GlsmSdlFbo0ViewportTest { private static final int FBO0_H = 96; private static final int VIEW = 32; - private static final int RED = 0xFFFF0000; - private static final int GREEN = 0xFF00FF00; - private static final int BLUE = 0xFF0000FF; - - private static FrameManager frameManager; - private static ResourceManager resourceManager; - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - frameManager = Reflect.get(BackendManager.RENDER_BACKEND, "frameManager"); - resourceManager = Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager"); - frameManager.destroyFinalTarget(); - frameManager.finalTarget().create(SDLGPUGate.device(), resourceManager, FBO0_W, FBO0_H, resourceManager.mapTextureFormat(GL11.GL_RGBA8)); - assertEquals(FBO0_H, frameManager.getFbo0Height()); + static void installFinalTarget() { + GlsmSdlHeadlessRig.installFinalTarget(FBO0_W, FBO0_H); } @AfterAll static void dropFinalTarget() { - GlsmSdlHeadlessRig.endFrame(); - if (frameManager != null) frameManager.destroyFinalTarget(); + GlsmSdlHeadlessRig.dropFinalTarget(); } private static void bindFbo0WithSmallViewport() { @@ -161,11 +147,11 @@ class GlsmSdlFbo0ViewportTest { GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, 0); - assertNull(resourceManager.getTextureMeta(copy), "destination was allocated before the copy"); + assertNull(GlsmSdlHeadlessRig.resourceManager().getTextureMeta(copy), "destination was allocated before the copy"); GLStateManager.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, 0, 0, VIEW, VIEW, 0); - final ResourceManager.TextureMeta meta = resourceManager.getTextureMeta(copy); + final ResourceManager.TextureMeta meta = GlsmSdlHeadlessRig.resourceManager().getTextureMeta(copy); assertNotNull(meta, "copyTexImage2D did not allocate the destination"); assertEquals(VIEW, meta.width()); assertEquals(VIEW, meta.height()); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlFfpTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlFfpTest.java index bfa78d5da..460ec1ab1 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlFfpTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlFfpTest.java @@ -10,17 +10,17 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; -import org.lwjgl.opengl.GL13; -import org.lwjgl.system.MemoryUtil; -import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.SIZE; +import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.TEXEL; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.describe; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.pixelAt; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -30,18 +30,14 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(GlsmSdlFrameExtension.class) class GlsmSdlFfpTest { - private static final int TEXEL = 0xFFFF4020; - private static final int LIGHTMAP_SIZE = 16; - private static int baseTexture; private static int lightmapTexture; @BeforeAll static void boot() { - GlsmSdlHeadlessRig.boot(); GlsmSdlHeadlessRig.beginFrame(); baseTexture = GlsmSdlHeadlessRig.createSolidTexture(TEXEL); - lightmapTexture = createLightmapTexture(); + lightmapTexture = GlsmSdlHeadlessRig.createLightmapTexture(true); GlsmSdlHeadlessRig.endFrame(); } @@ -53,37 +49,8 @@ class GlsmSdlFfpTest { FfpFixture.IntegerInstances.delete(); } - private static int createLightmapTexture() { - final int id = GLStateManager.glGenTextures(); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); - GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id); - GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); - GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); - GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, LIGHTMAP_SIZE, LIGHTMAP_SIZE, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null); - final ByteBuffer texels = MemoryUtil.memAlloc(LIGHTMAP_SIZE * LIGHTMAP_SIZE * 4); - try { - for (int y = 0; y < LIGHTMAP_SIZE; y++) { - for (int x = 0; x < LIGHTMAP_SIZE; x++) { - final boolean brightest = (x == 0 && y == 0) || (x == LIGHTMAP_SIZE - 1 && y == LIGHTMAP_SIZE - 1); - texels.put((byte) (brightest ? 255 : 0)); - texels.put((byte) 255); - texels.put((byte) (brightest ? 255 : 0)); - texels.put((byte) 255); - } - } - texels.flip(); - GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, LIGHTMAP_SIZE, LIGHTMAP_SIZE, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels); - } finally { - MemoryUtil.memFree(texels); - } - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - return id; - } - private static void bindBaseTexture() { - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, baseTexture); - GLStateManager.glEnable(GL11.GL_TEXTURE_2D); + GlsmSdlHeadlessRig.bindBaseTexture(baseTexture); } private static void enableLightmapUnit(boolean vanillaTextureMatrix) { @@ -100,13 +67,7 @@ class GlsmSdlFfpTest { } private static void quadTwoUnits(boolean vanillaBrightness) { - final short brightness = (short) (vanillaBrightness ? 240 : 0); - GLStateManager.glBegin(GL11.GL_QUADS); - GlsmSdlHeadlessRig.emitVertex(-1.0f, -1.0f, 0.0f, 0.0f, true, brightness); - GlsmSdlHeadlessRig.emitVertex(1.0f, -1.0f, 1.0f, 0.0f, true, brightness); - GlsmSdlHeadlessRig.emitVertex(1.0f, 1.0f, 1.0f, 1.0f, true, brightness); - GlsmSdlHeadlessRig.emitVertex(-1.0f, 1.0f, 0.0f, 1.0f, true, brightness); - GLStateManager.glEnd(); + GlsmSdlHeadlessRig.quadTwoUnits((short) (vanillaBrightness ? 240 : 0)); } private static void assertTexelEverywhere(String label) { @@ -124,22 +85,15 @@ class GlsmSdlFfpTest { assertTexelEverywhere("single unit"); } - @Test - void twoUnitTerrainQuadWithVanillaLightmapMatrixShowsTheTexel() { + /** Vanilla pairs its lightmap texture matrix with brightness 240; the identity matrix case uses brightness 0. */ + @ParameterizedTest(name = "vanilla lightmap matrix and brightness: {0}") + @ValueSource(booleans = {true, false}) + void twoUnitTerrainQuadShowsTheTexel(boolean vanilla) { bindBaseTexture(); - enableLightmapUnit(true); + enableLightmapUnit(vanilla); GlsmSdlHeadlessRig.terrainRenderState(); - quadTwoUnits(true); - assertTexelEverywhere("two units, vanilla lightmap matrix"); - } - - @Test - void twoUnitTerrainQuadWithIdentityLightmapMatrixShowsTheTexel() { - bindBaseTexture(); - enableLightmapUnit(false); - GlsmSdlHeadlessRig.terrainRenderState(); - quadTwoUnits(false); - assertTexelEverywhere("two units, identity lightmap matrix, zero brightness"); + quadTwoUnits(vanilla); + assertTexelEverywhere(vanilla ? "two units, vanilla lightmap matrix" : "two units, identity lightmap matrix, zero brightness"); } @Test @@ -226,9 +180,9 @@ class GlsmSdlFfpTest { final int[] pixels = GlsmSdlHeadlessRig.readTarget(); - GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 13, SIZE / 2, 0xFFFF0000, "entity -1 must render red"); - GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 32, SIZE / 2, 0xFF00FF00, "entity 7 must render green"); - GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 51, SIZE / 2, 0xFF0000FF, "entity 50020 must render blue"); + GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 13, SIZE / 2, GlsmSdlHeadlessRig.RED, "entity -1 must render red"); + GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 32, SIZE / 2, GlsmSdlHeadlessRig.GREEN, "entity 7 must render green"); + GlsmSdlHeadlessRig.assertPixel(pixels, SIZE, 51, SIZE / 2, GlsmSdlHeadlessRig.BLUE, "entity 50020 must render blue"); } private static void enableGuiStandardItemLighting() { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlFrameExtension.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlFrameExtension.java index fa263e19c..d9e304b38 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlFrameExtension.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlFrameExtension.java @@ -1,12 +1,18 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import static org.junit.jupiter.api.Assertions.assertTrue; -public final class GlsmSdlFrameExtension implements BeforeEachCallback, AfterEachCallback { +public final class GlsmSdlFrameExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback { + + @Override + public void beforeAll(ExtensionContext context) { + GlsmSdlHeadlessRig.boot(); + } @Override public void beforeEach(ExtensionContext context) { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlHeadlessRig.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlHeadlessRig.java index 727c883c9..1d0e38621 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlHeadlessRig.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlHeadlessRig.java @@ -9,6 +9,7 @@ import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate; import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; +import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineCache; import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager; import it.unimi.dsi.fastutil.ints.Int2IntMap; @@ -29,6 +30,14 @@ public final class GlsmSdlHeadlessRig { public static final int SIZE = 64; + public static final int RED = 0xFFFF0000; + public static final int GREEN = 0xFF00FF00; + public static final int BLUE = 0xFF0000FF; + public static final int BLACK = 0xFF000000; + + public static final int TEXEL = 0xFFFF4020; + public static final int LIGHTMAP_SIZE = 16; + private static boolean booted; private static int fbo; private static int colorTexture; @@ -91,9 +100,30 @@ public final class GlsmSdlHeadlessRig { return bound.get(target); } + public static FrameManager frameManager() { + return Reflect.get(BackendManager.RENDER_BACKEND, "frameManager"); + } + + public static ResourceManager resourceManager() { + return Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager"); + } + + /** Replaces FBO 0's backing target; pair with {@link #dropFinalTarget()}. */ + public static void installFinalTarget(int width, int height) { + final FrameManager fm = frameManager(); + final ResourceManager rm = resourceManager(); + fm.destroyFinalTarget(); + fm.finalTarget().create(SDLGPUGate.device(), rm, width, height, rm.mapTextureFormat(GL11.GL_RGBA8)); + assertEquals(height, fm.getFbo0Height()); + } + + public static void dropFinalTarget() { + endFrame(); + if (booted) frameManager().destroyFinalTarget(); + } + static int colorTargetSdlFormat() { - final ResourceManager rm = Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager"); - final ResourceManager.TextureMeta meta = rm.getTextureMeta(colorTexture); + final ResourceManager.TextureMeta meta = resourceManager().getTextureMeta(colorTexture); return meta != null ? meta.sdlFormat() : 0; } @@ -229,24 +259,69 @@ public final class GlsmSdlHeadlessRig { return fbo; } - public static int createSolidTexture(int argb) { - final int id = newNearestTexture(); - final ByteBuffer texels = MemoryUtil.memAlloc(2 * 2 * 4); + public static void putArgb(ByteBuffer texels, int index, int argb) { + texels.put(index * 4, (byte) ((argb >> 16) & 0xFF)); + texels.put(index * 4 + 1, (byte) ((argb >> 8) & 0xFF)); + texels.put(index * 4 + 2, (byte) (argb & 0xFF)); + texels.put(index * 4 + 3, (byte) ((argb >>> 24) & 0xFF)); + } + + /** Uploads one solid RGBA8 level of the GL_TEXTURE_2D bound on the active unit. */ + public static void uploadSolidLevel(int level, int size, int argb) { + final ByteBuffer texels = MemoryUtil.memAlloc(size * size * 4); try { - for (int i = 0; i < 4; i++) { - texels.put((byte) ((argb >> 16) & 0xFF)); - texels.put((byte) ((argb >> 8) & 0xFF)); - texels.put((byte) (argb & 0xFF)); - texels.put((byte) ((argb >>> 24) & 0xFF)); - } - texels.flip(); - GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, 2, 2, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels); + for (int i = 0; i < size * size; i++) putArgb(texels, i, argb); + GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, level, GL11.GL_RGBA8, size, size, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels); } finally { MemoryUtil.memFree(texels); } + } + + public static int createSolidTexture(int argb) { + final int id = newNearestTexture(); + uploadSolidLevel(0, 2, argb); return id; } + public static int createLightmapTexture(boolean brightCornersOnly) { + final int id = GLStateManager.glGenTextures(); + GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); + GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); + GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, LIGHTMAP_SIZE, LIGHTMAP_SIZE, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null); + final ByteBuffer texels = MemoryUtil.memAlloc(LIGHTMAP_SIZE * LIGHTMAP_SIZE * 4); + try { + final int last = LIGHTMAP_SIZE - 1; + for (int y = 0; y < LIGHTMAP_SIZE; y++) { + for (int x = 0; x < LIGHTMAP_SIZE; x++) { + final boolean bright = !brightCornersOnly || (x == 0 && y == 0) || (x == last && y == last); + putArgb(texels, y * LIGHTMAP_SIZE + x, bright ? 0xFFFFFFFF : GREEN); + } + } + GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, LIGHTMAP_SIZE, LIGHTMAP_SIZE, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels); + } finally { + MemoryUtil.memFree(texels); + } + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + return id; + } + + public static void bindBaseTexture(int texture) { + GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, texture); + GLStateManager.glEnable(GL11.GL_TEXTURE_2D); + } + + public static void quadTwoUnits(short brightness) { + GLStateManager.glBegin(GL11.GL_QUADS); + emitVertex(-1.0f, -1.0f, 0.0f, 0.0f, true, brightness); + emitVertex(1.0f, -1.0f, 1.0f, 0.0f, true, brightness); + emitVertex(1.0f, 1.0f, 1.0f, 1.0f, true, brightness); + emitVertex(-1.0f, 1.0f, 0.0f, 1.0f, true, brightness); + GLStateManager.glEnd(); + } + public static void enableLightmapUnit(int lightmapTexture, boolean vanillaTextureMatrix) { GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, lightmapTexture); @@ -339,19 +414,7 @@ public final class GlsmSdlHeadlessRig { GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL12.GL_TEXTURE_MAX_LEVEL, maxLevel); for (int level = 0; level <= maxLevel; level++) { - final int levelSize = Math.max(1, size >> level); - final ByteBuffer texels = MemoryUtil.memAlloc(levelSize * levelSize * 4); - try { - for (int i = 0; i < levelSize * levelSize; i++) { - texels.put(i * 4, (byte) ((argb >> 16) & 0xFF)); - texels.put(i * 4 + 1, (byte) ((argb >> 8) & 0xFF)); - texels.put(i * 4 + 2, (byte) (argb & 0xFF)); - texels.put(i * 4 + 3, (byte) ((argb >>> 24) & 0xFF)); - } - GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, level, GL11.GL_RGBA8, levelSize, levelSize, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels); - } finally { - MemoryUtil.memFree(texels); - } + uploadSolidLevel(level, Math.max(1, size >> level), argb); } return id; } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlIndexedBlendRestoreTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlIndexedBlendRestoreTest.java index ae395e54c..4426d2f59 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlIndexedBlendRestoreTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlIndexedBlendRestoreTest.java @@ -2,12 +2,10 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.RenderSystem; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; -import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend; import com.gtnewhorizons.angelica.sdlgpu.SdlAsserts; +import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineCache; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -22,14 +20,8 @@ import static org.lwjgl.sdl.SDLGPU.SDL_GPU_BLENDFACTOR_SRC_ALPHA; @ExtendWith(GlsmSdlFrameExtension.class) class GlsmSdlIndexedBlendRestoreTest { - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - private static PipelineCache pipeline() { - final ContextState st = Reflect.invokeStatic(SDLGPURenderBackend.class, "s", new Class[0]); - return st.pipeline; + return SdlTestRig.contextState().pipeline; } private static void vanillaBlendFunc() { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlInstancedConstantTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlInstancedConstantTest.java index 3306f62ff..d36fdca9e 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlInstancedConstantTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlInstancedConstantTest.java @@ -1,22 +1,20 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate; import com.gtnewhorizons.angelica.sdlgpu.device.Device; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL20; import org.lwjgl.system.MemoryUtil; import java.nio.FloatBuffer; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; /** * Metal steps a per-instance constant binding by 16 bytes per instance. @@ -48,29 +46,12 @@ class GlsmSdlInstancedConstantTest { } """; - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - - private static int compile(int type, String source) { - final int shader = GLStateManager.glCreateShader(type); - GLStateManager.glShaderSource(shader, source); - GLStateManager.glCompileShader(shader); - assertTrue(GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) != 0, () -> GLStateManager.glGetShaderInfoLog(shader)); - return shader; - } - @Test void everyInstanceReadsTheConstantWhenInstancesStepTheBinding() { final Device device = SDLGPUGate.device(); final String driver = device.getDriverName(); Reflect.set(device, "driverName", "metal"); - final int program = GLStateManager.glCreateProgram(); - GLStateManager.glAttachShader(program, compile(GL20.GL_VERTEX_SHADER, VERT)); - GLStateManager.glAttachShader(program, compile(GL20.GL_FRAGMENT_SHADER, FRAG)); - GLStateManager.glLinkProgram(program); - assertTrue(GLStateManager.glGetProgrami(program, GL20.GL_LINK_STATUS) != 0, "program did not link"); + final int program = GlTestSupport.program(VERT, FRAG); final int vao = GLStateManager.glGenVertexArrays(); final int vbo = GLStateManager.glGenBuffers(); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLWJGLServiceRoutingTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLWJGLServiceRoutingTest.java index 2054525d1..85ce7ecc4 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLWJGLServiceRoutingTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLWJGLServiceRoutingTest.java @@ -26,7 +26,6 @@ class GlsmSdlLWJGLServiceRoutingTest { @BeforeAll static void boot() { - GlsmSdlHeadlessRig.boot(); svc = SDLGPULWJGLService.create(); } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLegacyQueryTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLegacyQueryTest.java new file mode 100644 index 000000000..58be05eb8 --- /dev/null +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLegacyQueryTest.java @@ -0,0 +1,187 @@ +package com.gtnewhorizons.angelica.sdlgpu.glsm; + +import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.LegacyQueryFixture; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; +import org.lwjgl.opengl.GL14; +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL20; + +import java.nio.ByteBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.CLIENT_ARRAY_CAPS; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.HINT_TARGETS; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.assertEnableRoundTrip; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.drainClientAttribStack; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.hex; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.popAttribTo; +import static com.gtnewhorizons.angelica.glsm.LegacyQueryFixture.setEnabled; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Tag("glsm-sdl") +@ExtendWith(GlsmSdlFrameExtension.class) +class GlsmSdlLegacyQueryTest { + + private static final int[] CACHE_ONLY_CAPS = { GL20.GL_POINT_SPRITE, GL20.GL_VERTEX_PROGRAM_TWO_SIDE, GL13.GL_TEXTURE_CUBE_MAP }; + + // The SDL backend answers 0 or false for a pname it does not know, so a query that fell through to it + // fails the non-zero expectations below. + @Test + void legacyQueriesAreAnsweredWithoutTheBackend() { + final int base = GLStateManager.getAttribDepth(); + final IntBuffer ints = BufferUtils.createIntBuffer(16); + final FloatBuffer floats = BufferUtils.createFloatBuffer(16); + final DoubleBuffer doubles = BufferUtils.createDoubleBuffer(16); + final ByteBuffer bytes = BufferUtils.createByteBuffer(16); + final FloatBuffer positions = BufferUtils.createFloatBuffer(12); + final FloatBuffer other = BufferUtils.createFloatBuffer(12); + final FloatBuffer savedColor = BufferUtils.createFloatBuffer(16); + GLStateManager.glGetFloat(GL11.GL_CURRENT_COLOR, savedColor); + final boolean savedLighting = GLStateManager.glIsEnabled(GL11.GL_LIGHTING); + final boolean savedEdge = GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG); + final int[] savedHints = new int[HINT_TARGETS.length]; + for (int i = 0; i < HINT_TARGETS.length; i++) { + savedHints[i] = GLStateManager.glGetInteger(HINT_TARGETS[i]); + } + try { + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE1); + assertEquals(GL13.GL_TEXTURE1, GLStateManager.glGetInteger(GL13.GL_CLIENT_ACTIVE_TEXTURE)); + GLStateManager.glClientActiveTexture(0); + assertEquals(1, GLStateManager.getClientActiveTextureUnit(), "an invalid unit is rejected"); + assertEquals(GLStateManager.MAX_TEXTURE_COORDS, GLStateManager.glGetInteger(GL20.GL_MAX_TEXTURE_COORDS)); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + + for (final int cap : CLIENT_ARRAY_CAPS) { + final boolean was = GLStateManager.glIsEnabled(cap); + assertEnableRoundTrip(cap, true); + if (was) GLStateManager.glEnableClientState(cap); + } + for (final int cap : CACHE_ONLY_CAPS) { + final boolean was = GLStateManager.glIsEnabled(cap); + assertEnableRoundTrip(cap, false); + setEnabled(cap, was); + } + GLStateManager.glEdgeFlag(false); + assertFalse(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG)); + GLStateManager.glEdgeFlag(true); + assertTrue(GLStateManager.glGetBoolean(GL11.GL_EDGE_FLAG)); + + for (final int target : HINT_TARGETS) { + final String name = hex(target); + GLStateManager.glHint(target, GL11.GL_NICEST); + assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "set " + name); + assertEquals((float) GL11.GL_NICEST, GLStateManager.glGetFloat(target), "float " + name); + GLStateManager.glPushAttrib(GL11.GL_HINT_BIT); + GLStateManager.glHint(target, GL11.GL_FASTEST); + assertEquals(GL11.GL_FASTEST, GLStateManager.glGetInteger(target), "inside push " + name); + GLStateManager.glPopAttrib(); + assertEquals(GL11.GL_NICEST, GLStateManager.glGetInteger(target), "after pop " + name); + } + + GLStateManager.glVertexPointer(3, 12, positions); + GLStateManager.glFogCoordPointer(4, positions); + assertEquals(3, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_SIZE)); + assertEquals(GL11.GL_FLOAT, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_TYPE)); + assertEquals(12, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_STRIDE)); + assertEquals(0, GLStateManager.glGetInteger(GL15.GL_VERTEX_ARRAY_BUFFER_BINDING)); + assertEquals(4, GLStateManager.glGetInteger(GL14.GL_FOG_COORDINATE_ARRAY_STRIDE)); + assertEquals(3, GLStateManager.glGetInteger(GL14.GL_SECONDARY_COLOR_ARRAY_SIZE)); + GLStateManager.glPushClientAttrib(GL11.GL_CLIENT_VERTEX_ARRAY_BIT); + GLStateManager.glVertexPointer(2, 8, other); + GLStateManager.glFogCoordPointer(8, other); + GLStateManager.glEnableClientState(GL14.GL_FOG_COORDINATE_ARRAY); + assertEquals(2, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_SIZE)); + GLStateManager.glPopClientAttrib(); + assertEquals(3, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_SIZE), "client attrib pop restores the pointer"); + assertEquals(12, GLStateManager.glGetInteger(GL11.GL_VERTEX_ARRAY_STRIDE)); + assertEquals(4, GLStateManager.glGetInteger(GL14.GL_FOG_COORDINATE_ARRAY_STRIDE)); + assertFalse(GLStateManager.glIsEnabled(GL14.GL_FOG_COORDINATE_ARRAY)); + + GLStateManager.glColor4f(1.0f, 0.5f, 0.0f, 1.0f); + GLStateManager.glGetInteger(GL11.GL_CURRENT_COLOR, ints); + assertEquals(2147483647, ints.get(0)); + assertEquals(1073741823, ints.get(1)); + assertEquals(0, ints.get(2)); + GLStateManager.glEnable(GL11.GL_LIGHTING); + assertEquals(1, GLStateManager.glGetInteger(GL11.GL_LIGHTING)); + assertEquals(1.0f, GLStateManager.glGetFloat(GL11.GL_LIGHTING)); + assertEquals((float) GLStateManager.glGetInteger(GL11.GL_FOG_MODE), GLStateManager.glGetFloat(GL11.GL_FOG_MODE)); + assertEquals(GL11.GL_RENDER, GLStateManager.glGetInteger(GL11.GL_RENDER_MODE)); + + for (final int pname : LegacyQueryFixture.legacyPnames()) { + final String name = hex(pname); + final int scalar = GLStateManager.glGetInteger(pname); + final float scalarFloat = GLStateManager.glGetFloat(pname); + final boolean scalarBool = GLStateManager.glGetBoolean(pname); + ints.clear(); + GLStateManager.glGetInteger(pname, ints); + floats.clear(); + GLStateManager.glGetFloat(pname, floats); + doubles.clear(); + GLStateManager.glGetDouble(pname, doubles); + bytes.clear(); + GLStateManager.glGetBoolean(pname, bytes); + assertEquals(scalar, ints.get(0), "IntBuffer overload for " + name); + assertEquals(scalarFloat, floats.get(0), "FloatBuffer overload for " + name); + assertEquals((double) scalarFloat, doubles.get(0), "DoubleBuffer overload for " + name); + assertEquals(scalarBool, bytes.get(0) != 0, "ByteBuffer overload for " + name); + } + } finally { + drainClientAttribStack(); + popAttribTo(base); + GLStateManager.glClientActiveTexture(GL13.GL_TEXTURE0); + GLStateManager.glDisableClientState(GL14.GL_FOG_COORDINATE_ARRAY); + for (int i = 0; i < HINT_TARGETS.length; i++) { + GLStateManager.glHint(HINT_TARGETS[i], savedHints[i]); + } + GLStateManager.glEdgeFlag(savedEdge); + GLStateManager.glColor4f(savedColor.get(0), savedColor.get(1), savedColor.get(2), savedColor.get(3)); + setEnabled(GL11.GL_LIGHTING, savedLighting); + } + } + + @Test + void pointParametersRoundTrip() { + final FloatBuffer attenuation = BufferUtils.createFloatBuffer(16); + final float savedMin = GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN); + final float savedMax = GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX); + final float savedThreshold = GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE); + final int savedOrigin = GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN); + final int base = GLStateManager.getAttribDepth(); + try { + assertEquals(1.0f, savedMax); + GLStateManager.glPushAttrib(GL11.GL_POINT_BIT); + GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MIN, 2.0f); + GLStateManager.glPointParameterf(GL14.GL_POINT_SIZE_MAX, 8.0f); + GLStateManager.glPointParameterf(GL14.GL_POINT_FADE_THRESHOLD_SIZE, 3.0f); + GLStateManager.glPointParameteri(GL20.GL_POINT_SPRITE_COORD_ORIGIN, GL20.GL_LOWER_LEFT); + attenuation.clear(); + attenuation.put(0, 0.5f).put(1, 0.25f).put(2, 0.125f); + GLStateManager.glPointParameter(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation); + assertEquals(2.0f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN)); + assertEquals(8.0f, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX)); + assertEquals(3.0f, GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE)); + assertEquals(GL20.GL_LOWER_LEFT, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN)); + attenuation.clear(); + GLStateManager.glGetFloat(GL14.GL_POINT_DISTANCE_ATTENUATION, attenuation); + assertEquals(0.25f, attenuation.get(1)); + GLStateManager.glPopAttrib(); + assertEquals(savedMin, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MIN)); + assertEquals(savedMax, GLStateManager.glGetFloat(GL14.GL_POINT_SIZE_MAX)); + assertEquals(savedThreshold, GLStateManager.glGetFloat(GL14.GL_POINT_FADE_THRESHOLD_SIZE)); + assertEquals(savedOrigin, GLStateManager.glGetInteger(GL20.GL_POINT_SPRITE_COORD_ORIGIN)); + } finally { + popAttribTo(base); + } + } +} diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLogicOpTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLogicOpTest.java index d9f2952b2..a8eb04cb7 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLogicOpTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlLogicOpTest.java @@ -1,15 +1,14 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import com.gtnewhorizons.angelica.glsm.ffp.LogicOpFixture; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL30; import java.nio.ByteBuffer; @@ -20,7 +19,6 @@ import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.SIZE; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.describe; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.pixelAt; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; @Tag("glsm-sdl") @ExtendWith(GlsmSdlFrameExtension.class) @@ -58,11 +56,6 @@ class GlsmSdlLogicOpTest { private static int singleProgram; private static int mrtProgram; - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - @AfterEach void cleanup() { GLStateManager.glDisable(GL11.GL_COLOR_LOGIC_OP); @@ -183,23 +176,7 @@ class GlsmSdlLogicOpTest { } private static int buildProgram(int cached, String frag) { - if (cached != 0) return cached; - final int vs = compile(GL20.GL_VERTEX_SHADER, VERT); - final int fs = compile(GL20.GL_FRAGMENT_SHADER, frag); - final int p = GLStateManager.glCreateProgram(); - GLStateManager.glAttachShader(p, vs); - GLStateManager.glAttachShader(p, fs); - GLStateManager.glLinkProgram(p); - assertTrue(GLStateManager.glGetProgrami(p, GL20.GL_LINK_STATUS) != 0, "program did not link"); - return p; - } - - private static int compile(int type, String src) { - final int s = GLStateManager.glCreateShader(type); - GLStateManager.glShaderSource(s, src); - GLStateManager.glCompileShader(s); - assertTrue(GLStateManager.glGetShaderi(s, GL20.GL_COMPILE_STATUS) != 0, () -> "shader: " + GLStateManager.glGetShaderInfoLog(s)); - return s; + return cached != 0 ? cached : GlTestSupport.program(VERT, frag); } private static int centerPixel() { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlMipAttachmentTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlMipAttachmentTest.java index 6e6073662..4ea292917 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlMipAttachmentTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlMipAttachmentTest.java @@ -1,7 +1,6 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -11,6 +10,7 @@ import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; +import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.RED; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.assertUniform; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.createSolidMipTexture; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.readTextureLevel; @@ -23,12 +23,6 @@ class GlsmSdlMipAttachmentTest { private static final int MAX_LEVEL = 6; private static final int CLEAR = 0xFF204080; private static final int BASE = 0xFF10C030; - private static final int RED = 0xFFFF0000; - - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } private static void setClearColor(int argb) { GLStateManager.glClearColor(((argb >> 16) & 0xFF) / 255.0f, ((argb >> 8) & 0xFF) / 255.0f, (argb & 0xFF) / 255.0f, ((argb >>> 24) & 0xFF) / 255.0f); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlOffContextCreationTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlOffContextCreationTest.java index f7186347b..671b19e72 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlOffContextCreationTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlOffContextCreationTest.java @@ -2,32 +2,18 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.testutil.TestThreads; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @Tag("glsm-sdl") @ExtendWith(GlsmSdlFrameExtension.class) class GlsmSdlOffContextCreationTest { - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - @Test void creationWithoutContextThrows() throws InterruptedException { TestThreads.run("sdl-no-context", () -> assertThrows(IllegalStateException.class, GLStateManager::glGenTextures)); } - - @Test - void creationOnRenderThreadPasses() { - final int texture = GLStateManager.glGenTextures(); - assertNotEquals(0, texture); - GLStateManager.glDeleteTextures(texture); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlOutOfFrameExtension.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlOutOfFrameExtension.java index bce87ee49..5a78f2a6f 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlOutOfFrameExtension.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlOutOfFrameExtension.java @@ -1,22 +1,24 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; -import com.gtnewhorizons.angelica.glsm.backend.BackendManager; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; -import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -public final class GlsmSdlOutOfFrameExtension implements BeforeEachCallback, AfterEachCallback { +public final class GlsmSdlOutOfFrameExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback { + + @Override + public void beforeAll(ExtensionContext context) { + GlsmSdlHeadlessRig.boot(); + } @Override public void beforeEach(ExtensionContext context) { GlsmSdlHeadlessRig.endFrame(); - final FrameManager fm = Reflect.get(BackendManager.RENDER_BACKEND, "frameManager"); - assertFalse(fm.isFrameActive(), "test must start with no active frame"); + assertFalse(GlsmSdlHeadlessRig.frameManager().isFrameActive(), "test must start with no active frame"); } @Override diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlOutOfFrameReadbackTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlOutOfFrameReadbackTest.java index 60a90cd44..76a46fbf3 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlOutOfFrameReadbackTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlOutOfFrameReadbackTest.java @@ -1,10 +1,11 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; @@ -19,11 +20,6 @@ class GlsmSdlOutOfFrameReadbackTest { private static final int TEX_SIZE = 64; - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - @Test void outOfFrameTexImageReadback() { final ByteBuffer pattern = pattern(TEX_SIZE, TEX_SIZE, 1); @@ -33,15 +29,11 @@ class GlsmSdlOutOfFrameReadbackTest { GLStateManager.glDeleteTextures(tex); } - @Test - void inFrameCopyImageSubDataReadback() { - GlsmSdlHeadlessRig.beginFrame(); - copyAndReadBack(3); - } - - @Test - void outOfFrameCopyImageSubDataReadback() { - copyAndReadBack(4); + @ParameterizedTest(name = "in frame: {0}") + @ValueSource(booleans = {true, false}) + void copyImageSubDataReadback(boolean inFrame) { + if (inFrame) GlsmSdlHeadlessRig.beginFrame(); + copyAndReadBack(inFrame ? 3 : 4); } private static void copyAndReadBack(int seed) { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlProxyTextureProbeTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlProxyTextureProbeTest.java index 84aa8dc9d..ddafcd59a 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlProxyTextureProbeTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlProxyTextureProbeTest.java @@ -1,8 +1,6 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import com.gtnewhorizons.angelica.glsm.backend.BackendManager; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; @@ -11,11 +9,12 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL13; -import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; +import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.LIGHTMAP_SIZE; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.SIZE; +import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.TEXEL; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.describe; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.pixelAt; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -25,9 +24,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; @ExtendWith(GlsmSdlFrameExtension.class) class GlsmSdlProxyTextureProbeTest { - private static final int TEXEL = 0xFFFF4020; - private static final int LIGHTMAP_SIZE = 16; - private static final int[] PROXY_TARGETS = { 0x8063, 0x8064, 0x8070, 0x851B, 0x8C19, 0x8C1B, 0x84F7, 0x900B, 0x9101, 0x9103 }; @@ -37,10 +33,9 @@ class GlsmSdlProxyTextureProbeTest { @BeforeAll static void boot() { - GlsmSdlHeadlessRig.boot(); GlsmSdlHeadlessRig.beginFrame(); baseTexture = GlsmSdlHeadlessRig.createSolidTexture(TEXEL); - lightmapTexture = createLightmapTexture(); + lightmapTexture = GlsmSdlHeadlessRig.createLightmapTexture(false); GlsmSdlHeadlessRig.endFrame(); } @@ -49,7 +44,7 @@ class GlsmSdlProxyTextureProbeTest { GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, lightmapTexture); - final ResourceManager rm = resourceManager(); + final ResourceManager rm = GlsmSdlHeadlessRig.resourceManager(); final long handleBefore = rm.getTextureHandle(lightmapTexture); assertEquals(16384, vanillaGetGLMaximumTextureSize()); @@ -69,7 +64,7 @@ class GlsmSdlProxyTextureProbeTest { GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, lightmapTexture); - final ResourceManager rm = resourceManager(); + final ResourceManager rm = GlsmSdlHeadlessRig.resourceManager(); final long handleBefore = rm.getTextureHandle(lightmapTexture); final ResourceManager.TextureMeta before = rm.getTextureMeta(lightmapTexture); assertNotNull(before, "lightmap meta missing before the proxy calls"); @@ -97,15 +92,15 @@ class GlsmSdlProxyTextureProbeTest { @Test void proxyProbeDuringTerrainDrawStillShowsTheTexel() { - bindBaseTexture(); - enableLightmapUnit(); + GlsmSdlHeadlessRig.bindBaseTexture(baseTexture); + GlsmSdlHeadlessRig.enableLightmapUnit(lightmapTexture, true); GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); assertEquals(16384, vanillaGetGLMaximumTextureSize()); GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); GlsmSdlHeadlessRig.terrainRenderState(); - quadTwoUnits(); + GlsmSdlHeadlessRig.quadTwoUnits((short) 240); final int[] pixels = GlsmSdlHeadlessRig.readTarget(); final int center = pixelAt(pixels, SIZE, SIZE / 2, SIZE / 2); @@ -124,48 +119,4 @@ class GlsmSdlProxyTextureProbeTest { } return -1; } - - private static ResourceManager resourceManager() { - return Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager"); - } - - private static int createLightmapTexture() { - final int id = GLStateManager.glGenTextures(); - GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); - GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, id); - GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); - GLStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); - GLStateManager.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, LIGHTMAP_SIZE, LIGHTMAP_SIZE, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null); - final ByteBuffer texels = MemoryUtil.memAlloc(LIGHTMAP_SIZE * LIGHTMAP_SIZE * 4); - try { - for (int i = 0; i < LIGHTMAP_SIZE * LIGHTMAP_SIZE; i++) { - texels.put((byte) 255).put((byte) 255).put((byte) 255).put((byte) 255); - } - texels.flip(); - GLStateManager.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, LIGHTMAP_SIZE, LIGHTMAP_SIZE, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, texels); - } finally { - MemoryUtil.memFree(texels); - } - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - return id; - } - - private static void bindBaseTexture() { - GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); - GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, baseTexture); - GLStateManager.glEnable(GL11.GL_TEXTURE_2D); - } - - private static void enableLightmapUnit() { - GlsmSdlHeadlessRig.enableLightmapUnit(lightmapTexture, true); - } - - private static void quadTwoUnits() { - GLStateManager.glBegin(GL11.GL_QUADS); - GlsmSdlHeadlessRig.emitVertex(-1.0f, -1.0f, 0.0f, 0.0f, true, (short) 240); - GlsmSdlHeadlessRig.emitVertex(1.0f, -1.0f, 1.0f, 0.0f, true, (short) 240); - GlsmSdlHeadlessRig.emitVertex(1.0f, 1.0f, 1.0f, 1.0f, true, (short) 240); - GlsmSdlHeadlessRig.emitVertex(-1.0f, 1.0f, 0.0f, 1.0f, true, (short) 240); - GLStateManager.glEnd(); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlReadbackMatrixTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlReadbackMatrixTest.java index 475bc7b81..df061ea33 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlReadbackMatrixTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlReadbackMatrixTest.java @@ -2,23 +2,22 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.ReadbackFixture; -import com.gtnewhorizons.angelica.glsm.backend.BackendManager; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; -import com.gtnewhorizons.angelica.sdlgpu.SDLGPUGate; -import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; -import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL30; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -29,21 +28,14 @@ class GlsmSdlReadbackMatrixTest { private static final int FBO0_W = 8; private static final int FBO0_H = 6; - private static FrameManager frameManager; - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - frameManager = Reflect.get(BackendManager.RENDER_BACKEND, "frameManager"); - final ResourceManager resourceManager = Reflect.get(BackendManager.RENDER_BACKEND, "resourceManager"); - frameManager.destroyFinalTarget(); - frameManager.finalTarget().create(SDLGPUGate.device(), resourceManager, FBO0_W, FBO0_H, resourceManager.mapTextureFormat(GL11.GL_RGBA8)); + static void installFinalTarget() { + GlsmSdlHeadlessRig.installFinalTarget(FBO0_W, FBO0_H); } @AfterAll static void dropFinalTarget() { - GlsmSdlHeadlessRig.endFrame(); - if (frameManager != null) frameManager.destroyFinalTarget(); + GlsmSdlHeadlessRig.dropFinalTarget(); } @AfterEach @@ -52,59 +44,25 @@ class GlsmSdlReadbackMatrixTest { GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0); } - @Test - void rgba8() { - ReadbackFixture.rgba8(); + static Stream fixtureCases() { + return Stream.of( + Arguments.of("rgba8", (Runnable) ReadbackFixture::rgba8), + Arguments.of("packState", (Runnable) ReadbackFixture::packState), + Arguments.of("pixelPackBuffer", (Runnable) ReadbackFixture::pixelPackBuffer), + Arguments.of("rgb8", (Runnable) ReadbackFixture::rgb8), + Arguments.of("rgba16f", (Runnable) ReadbackFixture::rgba16f), + Arguments.of("rgba32f", (Runnable) ReadbackFixture::rgba32f), + Arguments.of("r8", (Runnable) ReadbackFixture::r8), + Arguments.of("rg16f", (Runnable) ReadbackFixture::rg16f), + Arguments.of("rgb10a2", (Runnable) ReadbackFixture::rgb10a2), + Arguments.of("r11g11b10f", (Runnable) ReadbackFixture::r11g11b10f), + Arguments.of("depthStencil", (Runnable) ReadbackFixture::depthStencil)); } - @Test - void packState() { - ReadbackFixture.packState(); - } - - @Test - void pixelPackBuffer() { - ReadbackFixture.pixelPackBuffer(); - } - - @Test - void rgb8() { - ReadbackFixture.rgb8(); - } - - @Test - void rgba16f() { - ReadbackFixture.rgba16f(); - } - - @Test - void rgba32f() { - ReadbackFixture.rgba32f(); - } - - @Test - void r8() { - ReadbackFixture.r8(); - } - - @Test - void rg16f() { - ReadbackFixture.rg16f(); - } - - @Test - void rgb10a2() { - ReadbackFixture.rgb10a2(); - } - - @Test - void r11g11b10f() { - ReadbackFixture.r11g11b10f(); - } - - @Test - void depthStencil() { - ReadbackFixture.depthStencil(); + @ParameterizedTest(name = "{0}") + @MethodSource("fixtureCases") + void sharedReadbackFixture(String name, Runnable fixtureCase) { + fixtureCase.run(); } @Test diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlReadbackTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlReadbackTest.java index ad6c884c6..bf9b279f6 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlReadbackTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlReadbackTest.java @@ -1,7 +1,6 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -25,11 +24,6 @@ class GlsmSdlReadbackTest { private static final int B = 0x56; private static final int A = 0xFF; - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - @Test void getTexImageSeesDeferredTextureUpload() { final int texture = GLStateManager.glGenTextures(); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlSamplerBindingTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlSamplerBindingTest.java index f57bd81ae..b5467f707 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlSamplerBindingTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlSamplerBindingTest.java @@ -4,7 +4,6 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.RenderSystem; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -18,11 +17,6 @@ class GlsmSdlSamplerBindingTest { private static final int UNIT = 3; - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - @Test void glsmDeleteClearsBothCaches() { final ContextState st = SdlTestRig.contextState(); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlSplashTargetTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlSplashTargetTest.java index 8279f6933..723a5c7ca 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlSplashTargetTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlSplashTargetTest.java @@ -9,7 +9,6 @@ import com.gtnewhorizons.angelica.sdlgpu.device.Device; import com.gtnewhorizons.angelica.sdlgpu.frame.OffscreenTarget; import com.gtnewhorizons.angelica.sdlgpu.resource.ResourceManager; import com.gtnewhorizons.angelica.sdlgpu.splash.SplashDispatcher; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -19,11 +18,6 @@ import org.lwjgl.opengl.GL30; @ExtendWith(GlsmSdlFrameExtension.class) class GlsmSdlSplashTargetTest { - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - @Test void seededSplashContextDrawsIntoSplashTarget() throws Exception { final SDLGPURenderBackend backend = (SDLGPURenderBackend) BackendManager.RENDER_BACKEND; @@ -39,7 +33,7 @@ class GlsmSdlSplashTargetTest { }); GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, target.fboId()); - GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), 0xFF00FF00, "splash target"); + GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), GlsmSdlHeadlessRig.GREEN, "splash target"); } finally { releaseSplashTarget(backend, target); } @@ -64,10 +58,10 @@ class GlsmSdlSplashTargetTest { }); GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, target.fboId()); - GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), 0xFF00FF00, "splash target"); + GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), GlsmSdlHeadlessRig.GREEN, "splash target"); GlsmSdlHeadlessRig.bindTarget(); - GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), 0xFF0000FF, "rig target"); + GlsmSdlHeadlessRig.assertUniform(GlsmSdlHeadlessRig.readTarget(), GlsmSdlHeadlessRig.BLUE, "rig target"); } finally { releaseSplashTarget(backend, target); } @@ -75,7 +69,7 @@ class GlsmSdlSplashTargetTest { private static OffscreenTarget createSplashTarget(SDLGPURenderBackend backend) { final Device device = Reflect.get(backend, "device"); - final ResourceManager resourceManager = Reflect.get(backend, "resourceManager"); + final ResourceManager resourceManager = GlsmSdlHeadlessRig.resourceManager(); final int sdlFormat = GlsmSdlHeadlessRig.colorTargetSdlFormat(); final OffscreenTarget target = new OffscreenTarget(); target.create(device, resourceManager, GlsmSdlHeadlessRig.SIZE, GlsmSdlHeadlessRig.SIZE, sdlFormat); @@ -86,7 +80,6 @@ class GlsmSdlSplashTargetTest { Reflect.set(backend, "splashTarget", null); SplashDispatcher.reset(); GlsmSdlHeadlessRig.bindTarget(); - final ResourceManager resourceManager = Reflect.get(backend, "resourceManager"); - target.destroy(resourceManager); + target.destroy(GlsmSdlHeadlessRig.resourceManager()); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlStreamingRingTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlStreamingRingTest.java index f8676f6bd..31da226a4 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlStreamingRingTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlStreamingRingTest.java @@ -6,7 +6,6 @@ import com.gtnewhorizons.angelica.glsm.ffp.FfpExtendedAttribs; import com.gtnewhorizons.angelica.glsm.streaming.PersistentStreamingBuffer; import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import it.unimi.dsi.fastutil.ints.IntSet; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -22,11 +21,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(GlsmSdlFrameExtension.class) class GlsmSdlStreamingRingTest { - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - @Test void ringCreationLeavesTheGlsmArrayBufferCacheMatchingSdl() { assertTrue(RenderSystem.supportsBufferStorage(), "SDL-GPU must advertise buffer storage"); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlTextureBindingQueryTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlTextureBindingQueryTest.java index 7945c24dd..d40fea556 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlTextureBindingQueryTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlTextureBindingQueryTest.java @@ -1,7 +1,6 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -19,11 +18,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @ExtendWith(GlsmSdlFrameExtension.class) class GlsmSdlTextureBindingQueryTest { - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - @Test void everyBindingQueryReadsThePerUnitSlot() { GLStateManager.glActiveTexture(GL13.GL_TEXTURE3); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlTextureStagingTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlTextureStagingTest.java index 24fee36b6..2546b4103 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlTextureStagingTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlTextureStagingTest.java @@ -42,10 +42,9 @@ class GlsmSdlTextureStagingTest { @BeforeAll static void boot() { - GlsmSdlHeadlessRig.boot(); final Object backend = BackendManager.RENDER_BACKEND; if (Reflect.get(backend, "transferThread") == null) { - final ResourceManager rm = Reflect.get(backend, "resourceManager"); + final ResourceManager rm = GlsmSdlHeadlessRig.resourceManager(); installed = new TransferThread(SDLGPUGate.device(), rm); Reflect.set(backend, "transferThread", installed); rm.setTransferThread(installed); @@ -60,7 +59,7 @@ class GlsmSdlTextureStagingTest { installed.getThread().join(); final Object backend = BackendManager.RENDER_BACKEND; Reflect.set(backend, "transferThread", null); - final ResourceManager rm = Reflect.get(backend, "resourceManager"); + final ResourceManager rm = GlsmSdlHeadlessRig.resourceManager(); rm.setTransferThread(null); installed = null; } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlUnalignedAttribTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlUnalignedAttribTest.java index 6cf58936e..665999cd3 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlUnalignedAttribTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlUnalignedAttribTest.java @@ -1,19 +1,17 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import org.junit.jupiter.api.BeforeAll; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL20; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; @Tag("glsm-sdl") @ExtendWith(GlsmSdlFrameExtension.class) @@ -45,32 +43,9 @@ class GlsmSdlUnalignedAttribTest { private static int program; - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - private static int buildProgram() { - if (program != 0) return program; - final int vs = GLStateManager.glCreateShader(GL20.GL_VERTEX_SHADER); - GLStateManager.glShaderSource(vs, VERT); - GLStateManager.glCompileShader(vs); - assertTrue(GLStateManager.glGetShaderi(vs, GL20.GL_COMPILE_STATUS) != 0, - () -> "vertex shader: " + GLStateManager.glGetShaderInfoLog(vs)); - - final int fs = GLStateManager.glCreateShader(GL20.GL_FRAGMENT_SHADER); - GLStateManager.glShaderSource(fs, FRAG); - GLStateManager.glCompileShader(fs); - assertTrue(GLStateManager.glGetShaderi(fs, GL20.GL_COMPILE_STATUS) != 0, - () -> "fragment shader: " + GLStateManager.glGetShaderInfoLog(fs)); - - final int p = GLStateManager.glCreateProgram(); - GLStateManager.glAttachShader(p, vs); - GLStateManager.glAttachShader(p, fs); - GLStateManager.glLinkProgram(p); - assertTrue(GLStateManager.glGetProgrami(p, GL20.GL_LINK_STATUS) != 0, "program did not link"); - program = p; - return p; + if (program == 0) program = GlTestSupport.program(VERT, FRAG); + return program; } private static int redChannelForFactorAt(int factorOffset, int decoyOffset) { @@ -116,16 +91,6 @@ class GlsmSdlUnalignedAttribTest { return (pixels[0] >> 16) & 0xFF; } - @Test - void byteAttribAtAnAlignedOffsetReadsItsOwnByte() { - assertEquals(FACTOR, redChannelForFactorAt(20, -1), 1); - } - - @Test - void byteAttribAtAnUnalignedOffsetReadsItsOwnByte() { - assertEquals(FACTOR, redChannelForFactorAt(23, 20), 1); - } - @Test void byteAttribAtEveryMisalignmentReadsItsOwnByte() { for (int offset = 20; offset <= 23; offset++) { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlVanillaTextureTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlVanillaTextureTest.java index 58eba97af..19b0c5de7 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlVanillaTextureTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlVanillaTextureTest.java @@ -44,7 +44,6 @@ class GlsmSdlVanillaTextureTest { @BeforeAll static void boot() { - GlsmSdlHeadlessRig.boot(); GlsmSdlHeadlessRig.beginFrame(); atlasTexture = createMipmappedAtlas(); lightmapTexture = allocateDynamicLightmap(); @@ -213,8 +212,7 @@ class GlsmSdlVanillaTextureTest { } } - @Test - void pushAttribWrappedCulledDrawKeepsFrontFace() { + private static int centerAfterPushAttribWrappedCulledQuad(boolean frontFacing) { uploadLightmapFrame(0xFFFFFFFF); bindAtlasUnit0(); enableLightmap(); @@ -223,27 +221,20 @@ class GlsmSdlVanillaTextureTest { GLStateManager.glEnable(GL11.GL_CULL_FACE); GLStateManager.glCullFace(GL11.GL_BACK); GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); - quad(1.0f, true, true); + quad(1.0f, true, frontFacing); GLStateManager.glPopAttrib(); - final int actual = center(); - assertColorNear(LEVEL_COLORS[0], actual, 2, "front-face winding kept"); + return center(); + } + + @Test + void pushAttribWrappedCulledDrawKeepsFrontFace() { + assertColorNear(LEVEL_COLORS[0], centerAfterPushAttribWrappedCulledQuad(true), 2, "front-face winding kept"); } @Test void pushAttribWrappedCulledDrawDropsBackFace() { - uploadLightmapFrame(0xFFFFFFFF); - bindAtlasUnit0(); - enableLightmap(); - GlsmSdlHeadlessRig.terrainRenderState(); - - GLStateManager.glEnable(GL11.GL_CULL_FACE); - GLStateManager.glCullFace(GL11.GL_BACK); - GLStateManager.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); - quad(1.0f, true, false); - GLStateManager.glPopAttrib(); - - final int actual = center(); + final int actual = centerAfterPushAttribWrappedCulledQuad(false); assertEquals(CLEAR_COLOR, actual, () -> "back-face winding was not culled: " + describe(actual)); } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlVoxelRegionBindTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlVoxelRegionBindTest.java index ed9e7e343..6d330a7ac 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlVoxelRegionBindTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlVoxelRegionBindTest.java @@ -3,7 +3,6 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -17,11 +16,6 @@ class GlsmSdlVoxelRegionBindTest { private static final int BOUND_SLOT = 5; - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - private static int glsmGenericSsbo() { return GlsmSdlHeadlessRig.glsmBufferBinding(GL43.GL_SHADER_STORAGE_BUFFER); } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlWideLineTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlWideLineTest.java index 5abc44b55..2b59a4365 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlWideLineTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/glsm/GlsmSdlWideLineTest.java @@ -2,12 +2,16 @@ package com.gtnewhorizons.angelica.sdlgpu.glsm; import com.gtnewhorizons.angelica.glsm.GLStateManager; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL11; +import java.util.stream.Stream; + import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.SIZE; import static com.gtnewhorizons.angelica.sdlgpu.glsm.GlsmSdlHeadlessRig.pixelAt; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -17,11 +21,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(GlsmSdlFrameExtension.class) class GlsmSdlWideLineTest { - @BeforeAll - static void boot() { - GlsmSdlHeadlessRig.boot(); - } - @AfterEach void resetLineState() { GLStateManager.glLineWidth(1.0f); @@ -94,14 +93,16 @@ class GlsmSdlWideLineTest { assertTrue(drawnRows >= SIZE / 4, () -> "line covers only " + drawnRows + " rows"); } - @Test - void lineEndingBehindTheCameraKeepsItsWidth() { - assertWidthAcrossTheNearPlane(IN_FRONT, BEHIND); + static Stream nearPlaneCrossings() { + return Stream.of( + Arguments.of("ending behind the camera", IN_FRONT, BEHIND), + Arguments.of("starting behind the camera", BEHIND, IN_FRONT)); } - @Test - void lineStartingBehindTheCameraKeepsItsWidth() { - assertWidthAcrossTheNearPlane(BEHIND, IN_FRONT); + @ParameterizedTest(name = "{0}") + @MethodSource("nearPlaneCrossings") + void lineCrossingTheNearPlaneKeepsItsWidth(String name, float[] start, float[] end) { + assertWidthAcrossTheNearPlane(start, end); } @Test diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/AttachmentClearTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/AttachmentClearTest.java index ab3c1e648..b0bd11a0c 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/AttachmentClearTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/AttachmentClearTest.java @@ -1,20 +1,18 @@ package com.gtnewhorizons.angelica.sdlgpu.pipeline; +import com.gtnewhorizons.angelica.sdlgpu.RealDeviceExtension; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; import com.gtnewhorizons.angelica.sdlgpu.resource.FboState; -import com.gtnewhorizons.angelica.sdlgpu.resource.PixelOps; import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL30; +import org.junit.jupiter.api.extension.ExtendWith; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; +@ExtendWith(RealDeviceExtension.class) class AttachmentClearTest { private static SdlTestRig rig; @@ -31,27 +29,19 @@ class AttachmentClearTest { @BeforeAll static void setUp() throws Exception { - rig = SdlTestRig.acquireRealDevice(); - rig.resourceManager.cachePreferredDepthFormats(); + rig = RealDeviceExtension.rig(); sm = new ShaderManager(rig.device); store = new PipelineStore(rig.device); attachmentClear = new AttachmentClear(rig.frameManager, store, sm); - colorTexture = rig.resourceManager.createTexture(9700, GL11.GL_TEXTURE_2D, GL11.GL_RGBA8, SIZE, SIZE, 1, 1); - assertTrue(colorTexture != 0, "color target creation failed"); - colorFormat = rig.resourceManager.getTextureMeta(9700).sdlFormat(); + final RealDeviceExtension.Target color = RealDeviceExtension.colorTarget(9700, SIZE, SIZE); + colorTexture = color.texture(); + colorFormat = color.sdlFormat(); PipelineCache.setSwapchainFormats(new int[]{ colorFormat }); - rig.resourceManager.createTexture(9701, GL11.GL_TEXTURE_2D, GL30.GL_DEPTH24_STENCIL8, SIZE, SIZE, 1, 1); - depthTexture = rig.resourceManager.ensureTextureUsage(9701, SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET); - assertTrue(depthTexture != 0, "depth+stencil target creation failed"); - depthFormat = rig.resourceManager.getTextureMeta(9701).sdlFormat(); - assertTrue(PixelOps.isDepthStencilFormat(depthFormat), "test needs a packed depth+stencil format"); - } - - @AfterAll - static void tearDown() { - SdlTestRig.releaseRealDevice(); + final RealDeviceExtension.Target depth = RealDeviceExtension.depthStencilTarget(9701, SIZE, SIZE); + depthTexture = depth.texture(); + depthFormat = depth.sdlFormat(); } private static FboState makeFbo() { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/BlendConstantsGuardTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/BlendConstantsGuardTest.java index f212f53b0..f34fc710f 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/BlendConstantsGuardTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/BlendConstantsGuardTest.java @@ -2,7 +2,14 @@ package com.gtnewhorizons.angelica.sdlgpu.pipeline; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.lwjgl.sdl.SDLGPU.*; @@ -18,41 +25,39 @@ class BlendConstantsGuardTest { return new PipelineCache(); } - @Test - void freshCacheDoesNotUseBlendConstants() { - final PipelineCache pc = createCache(); - assertFalse(pc.usesBlendConstants()); + private static Arguments state(String name, boolean expected, Consumer setup) { + return Arguments.of(name, expected, setup); } - @Test - void constantColorFactorWithBlendDisabledDoesNotUseBlendConstants() { - final PipelineCache pc = createCache(); - pc.setBlendFactors(SDL_GPU_BLENDFACTOR_CONSTANT_COLOR, SDL_GPU_BLENDFACTOR_ZERO, SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ZERO); - assertFalse(pc.usesBlendConstants(), "blend is disabled on every draw buffer; the constant is never sampled"); + static Stream states() { + return Stream.of( + state("fresh cache", false, pc -> {}), + state("constant color factor, blend disabled on every draw buffer", false, + pc -> pc.setBlendFactors(SDL_GPU_BLENDFACTOR_CONSTANT_COLOR, SDL_GPU_BLENDFACTOR_ZERO, SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ZERO)), + state("constant color factor, blend enabled", true, pc -> { + pc.blendEnabledPerDrawBuffer[0] = true; + pc.setBlendFactors(SDL_GPU_BLENDFACTOR_CONSTANT_COLOR, SDL_GPU_BLENDFACTOR_ZERO, SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ZERO); + }), + state("one-minus-constant-color factor, blend enabled", true, pc -> { + pc.blendEnabledPerDrawBuffer[0] = true; + pc.setBlendFactors(SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR, SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ZERO); + }), + state("non-constant factors, blend enabled", false, pc -> { + pc.blendEnabledPerDrawBuffer[0] = true; + pc.setBlendFactors(SDL_GPU_BLENDFACTOR_SRC_ALPHA, SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ZERO); + }), + state("constant color factor on draw buffer 1 only", true, pc -> { + pc.blendEnabledPerDrawBuffer[1] = true; + pc.setBlendFactors(1, SDL_GPU_BLENDFACTOR_CONSTANT_COLOR, SDL_GPU_BLENDFACTOR_ZERO, SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ZERO); + })); } - @Test - void constantColorFactorWithBlendEnabledUsesBlendConstants() { + @ParameterizedTest(name = "{0}") + @MethodSource("states") + void usesBlendConstants(String name, boolean expected, Consumer setup) { final PipelineCache pc = createCache(); - pc.blendEnabledPerDrawBuffer[0] = true; - pc.setBlendFactors(SDL_GPU_BLENDFACTOR_CONSTANT_COLOR, SDL_GPU_BLENDFACTOR_ZERO, SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ZERO); - assertTrue(pc.usesBlendConstants()); - } - - @Test - void oneMinusConstantColorFactorAlsoUsesBlendConstants() { - final PipelineCache pc = createCache(); - pc.blendEnabledPerDrawBuffer[0] = true; - pc.setBlendFactors(SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR, SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ZERO); - assertTrue(pc.usesBlendConstants()); - } - - @Test - void nonConstantFactorsWithBlendEnabledDoNotUseBlendConstants() { - final PipelineCache pc = createCache(); - pc.blendEnabledPerDrawBuffer[0] = true; - pc.setBlendFactors(SDL_GPU_BLENDFACTOR_SRC_ALPHA, SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ZERO); - assertFalse(pc.usesBlendConstants()); + setup.accept(pc); + assertEquals(expected, pc.usesBlendConstants()); } @Test @@ -66,12 +71,4 @@ class BlendConstantsGuardTest { pc.markOutputDirty(); assertFalse(pc.usesBlendConstants()); } - - @Test - void secondDrawBufferAloneCanTriggerBlendConstants() { - final PipelineCache pc = createCache(); - pc.blendEnabledPerDrawBuffer[1] = true; - pc.setBlendFactors(1, SDL_GPU_BLENDFACTOR_CONSTANT_COLOR, SDL_GPU_BLENDFACTOR_ZERO, SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ZERO); - assertTrue(pc.usesBlendConstants(), "the loop must scan every draw buffer slot, not only slot 0"); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/ClearValueLayoutHashTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/ClearValueLayoutHashTest.java index 54c1a0873..8e8d9d9b7 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/ClearValueLayoutHashTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/ClearValueLayoutHashTest.java @@ -5,7 +5,6 @@ import org.junit.jupiter.api.Test; import static com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineApplier.foldClearColor; import static com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineApplier.foldClearDepth; import static com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineApplier.foldClearStencil; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; class ClearValueLayoutHashTest { @@ -17,11 +16,6 @@ class ClearValueLayoutHashTest { assertNotEquals(foldClearDepth(SEED, 0.0f), foldClearDepth(SEED, 1.0f), "the minimap mask clear must not reuse the world's depth-1 clear target"); } - @Test - void depthFoldIsStableForTheSameValue() { - assertEquals(foldClearDepth(SEED, 0.0f), foldClearDepth(SEED, 0.0f)); - } - @Test void negativeZeroIsDistinguishedFromZero() { assertNotEquals(foldClearDepth(SEED, 0.0f), foldClearDepth(SEED, -0.0f), "raw bits, not numeric equality, so the cached value always matches what was baked"); @@ -39,6 +33,5 @@ class ClearValueLayoutHashTest { @Test void stencilValuesDoNotCollide() { assertNotEquals(foldClearStencil(SEED, 0), foldClearStencil(SEED, 1)); - assertEquals(foldClearStencil(SEED, 0xFF), foldClearStencil(SEED, 0xFF)); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/DepthStencilReadbackTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/DepthStencilReadbackTest.java index 52aa05e7d..cf1e3d09a 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/DepthStencilReadbackTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/DepthStencilReadbackTest.java @@ -1,11 +1,11 @@ package com.gtnewhorizons.angelica.sdlgpu.pipeline; +import com.gtnewhorizons.angelica.sdlgpu.RealDeviceExtension; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; import com.gtnewhorizons.angelica.sdlgpu.resource.FBOClearTracker; import com.gtnewhorizons.angelica.sdlgpu.resource.FboState; -import com.gtnewhorizons.angelica.sdlgpu.resource.PixelOps; import com.gtnewhorizons.angelica.sdlgpu.resource.TextureOps; import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager; import com.gtnewhorizons.angelica.sdlgpu.util.MemoryAccess; @@ -14,8 +14,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL30; +import org.junit.jupiter.api.extension.ExtendWith; import org.lwjgl.sdl.SDL_GPUDepthStencilTargetInfo; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; @@ -27,9 +26,9 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_LOADOP_LOAD; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_STOREOP_STORE; -import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; import static org.lwjgl.system.MemoryStack.stackPush; +@ExtendWith(RealDeviceExtension.class) @Tag("glsm-sdl") @Timeout(120) class DepthStencilReadbackTest { @@ -55,9 +54,8 @@ class DepthStencilReadbackTest { @BeforeAll static void setUp() throws Exception { - rig = SdlTestRig.acquireRealDevice(); + rig = RealDeviceExtension.rig(); frameManager = rig.frameManager; - rig.resourceManager.cachePreferredDepthFormats(); sm = new ShaderManager(rig.device); store = new PipelineStore(rig.device); clearTracker = new FBOClearTracker(frameManager, rig.resourceManager, sm); @@ -66,11 +64,9 @@ class DepthStencilReadbackTest { readback = new DepthStencilReadback(frameManager, rig.resourceManager, store, sm, clearTracker); st = new ContextState(); - rig.resourceManager.createTexture(9801, GL11.GL_TEXTURE_2D, GL30.GL_DEPTH24_STENCIL8, W, H, 1, 1); - depthTexture = rig.resourceManager.ensureTextureUsage(9801, SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET); - assertNotEquals(0L, depthTexture, "depth+stencil texture creation failed"); - depthFormat = rig.resourceManager.getTextureMeta(9801).sdlFormat(); - assertTrue(PixelOps.isDepthStencilFormat(depthFormat), "test needs a packed depth+stencil format"); + final RealDeviceExtension.Target depth = RealDeviceExtension.depthStencilTarget(9801, W, H); + depthTexture = depth.texture(); + depthFormat = depth.sdlFormat(); } @AfterAll @@ -81,7 +77,6 @@ class DepthStencilReadbackTest { readback.shutdown(); } if (store != null) store.shutdown(); - SdlTestRig.releaseRealDevice(); } private static void clearRegion(float depth, int stencil) { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PipelineCacheKeyTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PipelineCacheKeyTest.java index e7bbc1fab..d8f233b2a 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PipelineCacheKeyTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PipelineCacheKeyTest.java @@ -1,13 +1,16 @@ package com.gtnewhorizons.angelica.sdlgpu.pipeline; - import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import java.lang.reflect.Method; import java.util.Arrays; +import java.util.function.Consumer; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; import static org.lwjgl.sdl.SDLGPU.*; @@ -30,34 +33,47 @@ class PipelineCacheKeyTest { assertEquals(SDL_GPU_COMPAREOP_LESS, st.pipeline.depthCompareOp); } - @Test - void testIdenticalStateProducesSameKey() { - final PipelineCache a = createCache(); - final PipelineCache b = createCache(); + private static final Consumer NONE = c -> {}; - final long keyA = computeKey(a); - final long keyB = computeKey(b); - assertEquals(keyA, keyB, "Identical state should produce identical keys"); + private static Arguments change(String name, Consumer shared, Consumer onlyB) { + return Arguments.of(name, shared, onlyB); } - @Test - void testBlendChangeProducesDifferentKey() { - final PipelineCache a = createCache(); - final PipelineCache b = createCache(); - b.blendEnabledPerDrawBuffer[0] = true; - - assertNotEquals(computeKey(a), computeKey(b), "Blend state change should produce different key"); + static Stream keyChangingState() { + return Stream.of( + change("blend enable", NONE, b -> b.blendEnabledPerDrawBuffer[0] = true), + change("depth test with a depth target", c -> c.hasDepthTarget = true, b -> b.depthTestEnabled = true), + change("cull enable", NONE, b -> b.cullEnabled = true), + change("primitive type", NONE, b -> b.primitiveType = SDL_GPU_PRIMITIVETYPE_LINELIST), + change("blend factors", NONE, b -> b.setBlendFactors(SDL_GPU_BLENDFACTOR_SRC_ALPHA, SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ZERO)), + change("color write mask", NONE, b -> b.colorWriteMask = SDL_GPU_COLORCOMPONENT_R | SDL_GPU_COLORCOMPONENT_G | SDL_GPU_COLORCOMPONENT_B), + change("shaders", NONE, b -> { + b.vertexShader = 12345L; + b.fragmentShader = 67890L; + }), + change("depth compare op", NONE, b -> b.depthCompareOp = SDL_GPU_COMPAREOP_LESS_OR_EQUAL), + change("color target format", NONE, b -> b.setColorTargetFormats(new int[]{SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT})), + change("depth bias with polygon offset on", c -> c.polygonOffsetFillEnabled = true, b -> { + b.depthBiasConstant = 1.0f; + b.depthBiasSlopeFactor = 2.0f; + }), + change("depth bias clamp with polygon offset on", c -> { + c.polygonOffsetFillEnabled = true; + c.depthBiasConstant = 1.0f; + c.depthBiasSlopeFactor = 2.0f; + }, b -> b.depthBiasClamp = 0.5f)); } - @Test - void testDepthTestChangeProducesDifferentKey() { + @ParameterizedTest(name = "{0}") + @MethodSource("keyChangingState") + void stateChangeProducesDifferentKey(String name, Consumer shared, Consumer onlyB) { final PipelineCache a = createCache(); final PipelineCache b = createCache(); - a.hasDepthTarget = true; - b.hasDepthTarget = true; - b.depthTestEnabled = true; + shared.accept(a); + shared.accept(b); + onlyB.accept(b); - assertNotEquals(computeKey(a), computeKey(b), "Depth test change should produce different key"); + assertNotEquals(computeKey(a), computeKey(b), name + " must change the pipeline key"); } @Test @@ -69,15 +85,6 @@ class PipelineCacheKeyTest { assertEquals(computeKey(a), computeKey(b), "Depth test without DSV must produce same key"); } - @Test - void testCullEnableChangeProducesDifferentKey() { - final PipelineCache a = createCache(); - final PipelineCache b = createCache(); - b.cullEnabled = true; - - assertNotEquals(computeKey(a), computeKey(b), "Enabling cull should produce different key"); - } - @Test void testCullFaceChangeOnlyMattersWhenEnabled() { final PipelineCache a = createCache(); @@ -91,24 +98,6 @@ class PipelineCacheKeyTest { "cullFaceMode change should change key when culling is enabled"); } - @Test - void testPrimitiveTypeChangeProducesDifferentKey() { - final PipelineCache a = createCache(); - final PipelineCache b = createCache(); - b.primitiveType = SDL_GPU_PRIMITIVETYPE_LINELIST; - - assertNotEquals(computeKey(a), computeKey(b), "Primitive type change should produce different key"); - } - - @Test - void testBlendFactorChangeProducesDifferentKey() { - final PipelineCache a = createCache(); - final PipelineCache b = createCache(); - b.setBlendFactors(SDL_GPU_BLENDFACTOR_SRC_ALPHA, SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_GPU_BLENDFACTOR_ONE, SDL_GPU_BLENDFACTOR_ZERO); - - assertNotEquals(computeKey(a), computeKey(b), "Blend factor change should produce different key"); - } - @Test void perDrawBufferFactorOnLiveSlotChangesKey() { final PipelineCache a = createCache(); @@ -173,43 +162,6 @@ class PipelineCacheKeyTest { assertEquals(computeKey(base), computeKey(attachment3), "index 3 is a draw buffer past the two targets"); } - @Test - void testColorWriteMaskChangeProducesDifferentKey() { - final PipelineCache a = createCache(); - final PipelineCache b = createCache(); - b.colorWriteMask = SDL_GPU_COLORCOMPONENT_R | SDL_GPU_COLORCOMPONENT_G | SDL_GPU_COLORCOMPONENT_B; - - assertNotEquals(computeKey(a), computeKey(b), "Color write mask change should produce different key"); - } - - @Test - void testShaderChangeProducesDifferentKey() { - final PipelineCache a = createCache(); - final PipelineCache b = createCache(); - b.vertexShader = 12345L; - b.fragmentShader = 67890L; - - assertNotEquals(computeKey(a), computeKey(b), "Shader change should produce different key"); - } - - @Test - void testDepthCompareOpChangeProducesDifferentKey() { - final PipelineCache a = createCache(); - final PipelineCache b = createCache(); - b.depthCompareOp = SDL_GPU_COMPAREOP_LESS_OR_EQUAL; - - assertNotEquals(computeKey(a), computeKey(b), "Depth compare op change should produce different key"); - } - - @Test - void testColorTargetFormatChangeProducesDifferentKey() { - final PipelineCache a = createCache(); - final PipelineCache b = createCache(); - b.setColorTargetFormats(new int[]{SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT}); - - assertNotEquals(computeKey(a), computeKey(b), "Color target format change should produce different key"); - } - @Test void polygonOffsetEnableChangesKeyForFilledTrianglesOnly() { final PipelineCache a = createCache(); @@ -228,17 +180,6 @@ class PipelineCacheKeyTest { assertEquals(computeKey(a), computeKey(b), "polygon offset fill must not bias wireframe polygons"); } - @Test - void testDepthBiasChangeProducesDifferentKey() { - final PipelineCache a = createCache(); - final PipelineCache b = createCache(); - a.polygonOffsetFillEnabled = b.polygonOffsetFillEnabled = true; - b.depthBiasConstant = 1.0f; - b.depthBiasSlopeFactor = 2.0f; - - assertNotEquals(computeKey(a), computeKey(b), "Depth bias change should produce different key"); - } - @Test void depthBiasValuesShareAPipelineWhileOffsetIsOff() { final PipelineCache a = createCache(); @@ -250,25 +191,7 @@ class PipelineCacheKeyTest { assertEquals(computeKey(a), computeKey(b), "glPolygonOffset with the offset disabled must not build a new pipeline"); } - @Test - void testDepthBiasClampProducesDifferentKey() { - final PipelineCache a = createCache(); - final PipelineCache b = createCache(); - a.polygonOffsetFillEnabled = b.polygonOffsetFillEnabled = true; - a.depthBiasConstant = b.depthBiasConstant = 1.0f; - a.depthBiasSlopeFactor = b.depthBiasSlopeFactor = 2.0f; - b.depthBiasClamp = 0.5f; - - assertNotEquals(computeKey(a), computeKey(b), "two draws differing only in polygon-offset clamp must not share a pipeline"); - } - - private long computeKey(PipelineCache cache) { - try { - final Method m = PipelineCache.class.getDeclaredMethod("computeKey", PipelineStore.class, ContextState.class); - m.setAccessible(true); - return (long) m.invoke(cache, (PipelineStore) null, (ContextState) null); - } catch (Exception e) { - throw new RuntimeException("Failed to invoke computeKey", e); - } + private static long computeKey(PipelineCache cache) { + return cache.computeKey(null, null); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PipelineCacheVertexFormatTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PipelineCacheVertexFormatTest.java index aa627fd64..ea85d8eea 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PipelineCacheVertexFormatTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PipelineCacheVertexFormatTest.java @@ -1,9 +1,14 @@ package com.gtnewhorizons.angelica.sdlgpu.pipeline; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL30; +import java.util.stream.Stream; + import static org.junit.jupiter.api.Assertions.*; import static org.lwjgl.sdl.SDLGPU.*; @@ -57,113 +62,35 @@ class PipelineCacheVertexFormatTest { assertEquals(PipelineCache.INVALID_FMT, PipelineCache.requestedVertexFormat(4, GL11.GL_DOUBLE, false)); } - @Test - void testFloat1() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_FLOAT, mapVertexFormat(1, GL11.GL_FLOAT, false)); + static Stream formats() { + return Stream.of( + Arguments.of("Float1", SDL_GPU_VERTEXELEMENTFORMAT_FLOAT, 1, GL11.GL_FLOAT, false, false), + Arguments.of("Float2", SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, 2, GL11.GL_FLOAT, false, false), + Arguments.of("Float3", SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3, 3, GL11.GL_FLOAT, false, false), + Arguments.of("Float4", SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4, 4, GL11.GL_FLOAT, false, false), + Arguments.of("UByte4", SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM, 4, GL11.GL_UNSIGNED_BYTE, false, false), + Arguments.of("UByte4Norm", SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM, 4, GL11.GL_UNSIGNED_BYTE, true, false), + Arguments.of("UByte4Integer", SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4, 4, GL11.GL_UNSIGNED_BYTE, false, true), + Arguments.of("Short2", SDL_GPU_VERTEXELEMENTFORMAT_SHORT2_NORM, 2, GL11.GL_SHORT, false, false), + Arguments.of("Short2Norm", SDL_GPU_VERTEXELEMENTFORMAT_SHORT2_NORM, 2, GL11.GL_SHORT, true, false), + Arguments.of("Short4", SDL_GPU_VERTEXELEMENTFORMAT_SHORT4_NORM, 4, GL11.GL_SHORT, false, false), + Arguments.of("Short4Integer", SDL_GPU_VERTEXELEMENTFORMAT_SHORT4, 4, GL11.GL_SHORT, false, true), + Arguments.of("Int1", SDL_GPU_VERTEXELEMENTFORMAT_INT, 1, GL11.GL_INT, false, false), + Arguments.of("Int4", SDL_GPU_VERTEXELEMENTFORMAT_INT4, 4, GL11.GL_INT, false, false), + Arguments.of("UInt2", SDL_GPU_VERTEXELEMENTFORMAT_UINT2, 2, GL11.GL_UNSIGNED_INT, false, false), + Arguments.of("Half2", SDL_GPU_VERTEXELEMENTFORMAT_HALF2, 2, GL30.GL_HALF_FLOAT, false, false), + Arguments.of("Half4", SDL_GPU_VERTEXELEMENTFORMAT_HALF4, 4, GL30.GL_HALF_FLOAT, false, false), + Arguments.of("Byte4", SDL_GPU_VERTEXELEMENTFORMAT_BYTE4_NORM, 4, GL11.GL_BYTE, false, false), + Arguments.of("Byte4Norm", SDL_GPU_VERTEXELEMENTFORMAT_BYTE4_NORM, 4, GL11.GL_BYTE, true, false), + Arguments.of("Byte4Integer", SDL_GPU_VERTEXELEMENTFORMAT_BYTE4, 4, GL11.GL_BYTE, false, true), + Arguments.of("UShort2Norm", SDL_GPU_VERTEXELEMENTFORMAT_USHORT2_NORM, 2, GL11.GL_UNSIGNED_SHORT, true, false), + Arguments.of("UShort4", SDL_GPU_VERTEXELEMENTFORMAT_USHORT4_NORM, 4, GL11.GL_UNSIGNED_SHORT, false, false), + Arguments.of("UShort4Integer", SDL_GPU_VERTEXELEMENTFORMAT_USHORT4, 4, GL11.GL_UNSIGNED_SHORT, false, true)); } - @Test - void testFloat2() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, mapVertexFormat(2, GL11.GL_FLOAT, false)); - } - - @Test - void testFloat3() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3, mapVertexFormat(3, GL11.GL_FLOAT, false)); - } - - @Test - void testFloat4() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4, mapVertexFormat(4, GL11.GL_FLOAT, false)); - } - - @Test - void testUByte4() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM, mapVertexFormat(4, GL11.GL_UNSIGNED_BYTE, false)); - } - - @Test - void testUByte4Norm() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM, mapVertexFormat(4, GL11.GL_UNSIGNED_BYTE, true)); - } - - @Test - void testUByte4Integer() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4, PipelineCache.mapVertexFormat(4, GL11.GL_UNSIGNED_BYTE, false, true)); - } - - @Test - void testShort2() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_SHORT2_NORM, mapVertexFormat(2, GL11.GL_SHORT, false)); - } - - @Test - void testShort2Norm() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_SHORT2_NORM, mapVertexFormat(2, GL11.GL_SHORT, true)); - } - - @Test - void testShort4() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_SHORT4_NORM, mapVertexFormat(4, GL11.GL_SHORT, false)); - } - - @Test - void testShort4Integer() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_SHORT4, PipelineCache.mapVertexFormat(4, GL11.GL_SHORT, false, true)); - } - - @Test - void testInt1() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_INT, mapVertexFormat(1, GL11.GL_INT, false)); - } - - @Test - void testInt4() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_INT4, mapVertexFormat(4, GL11.GL_INT, false)); - } - - @Test - void testUInt2() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_UINT2, mapVertexFormat(2, GL11.GL_UNSIGNED_INT, false)); - } - - @Test - void testHalf2() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_HALF2, mapVertexFormat(2, GL30.GL_HALF_FLOAT, false)); - } - - @Test - void testHalf4() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_HALF4, mapVertexFormat(4, GL30.GL_HALF_FLOAT, false)); - } - - @Test - void testByte4() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_BYTE4_NORM, mapVertexFormat(4, GL11.GL_BYTE, false)); - } - - @Test - void testByte4Norm() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_BYTE4_NORM, mapVertexFormat(4, GL11.GL_BYTE, true)); - } - - @Test - void testByte4Integer() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_BYTE4, PipelineCache.mapVertexFormat(4, GL11.GL_BYTE, false, true)); - } - - @Test - void testUShort2Norm() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_USHORT2_NORM, mapVertexFormat(2, GL11.GL_UNSIGNED_SHORT, true)); - } - - @Test - void testUShort4() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_USHORT4_NORM, mapVertexFormat(4, GL11.GL_UNSIGNED_SHORT, false)); - } - - @Test - void testUShort4Integer() { - assertEquals(SDL_GPU_VERTEXELEMENTFORMAT_USHORT4, PipelineCache.mapVertexFormat(4, GL11.GL_UNSIGNED_SHORT, false, true)); + @ParameterizedTest(name = "{0}") + @MethodSource("formats") + void mapsToSdlFormat(String name, int expected, int size, int glType, boolean normalized, boolean integer) { + assertEquals(expected, PipelineCache.mapVertexFormat(size, glType, normalized, integer)); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PipelineCacheVertexInputTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PipelineCacheVertexInputTest.java index 77e038c80..e7fd01e12 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PipelineCacheVertexInputTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PipelineCacheVertexInputTest.java @@ -43,6 +43,21 @@ class PipelineCacheVertexInputTest { private static int nextTestSetId = 1; + /** Shader input vec size per location; 0 leaves the location unused. */ + private static int[] vecSizes(int... byLocation) { + final int[] out = new int[ContextState.MAX_VERTEX_ATTRIBS]; + System.arraycopy(byLocation, 0, out, 0, byLocation.length); + return out; + } + + private static int[] fp32(int[] vecSizes) { + final int[] out = new int[ContextState.MAX_VERTEX_ATTRIBS]; + for (int i = 0; i < out.length; i++) { + if (vecSizes[i] != 0) out[i] = Spvc.SPVC_BASETYPE_FP32; + } + return out; + } + private static PipelineCache cache(int shaderInputMask, int[] vecSizes, int[] baseTypes) { return cache(nextTestSetId++, shaderInputMask, vecSizes, baseTypes); } @@ -95,10 +110,8 @@ class PipelineCacheVertexInputTest { } private static PipelineCache coercionCache(String name) { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 4; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(4); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(1, vecSizes, baseTypes); final String[] names = new String[ContextState.MAX_VERTEX_ATTRIBS]; names[0] = name; @@ -179,10 +192,8 @@ class PipelineCacheVertexInputTest { @Test void sparseLayout_locations0and2_buildsDenseBindingArray() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; vecSizes[2] = 2; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; baseTypes[2] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3, 0, 2); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x5, vecSizes, baseTypes); final ContextState cs = new ContextState(); @@ -235,11 +246,8 @@ class PipelineCacheVertexInputTest { @Test void correctedDhGenericLayout_buildsSuccessfully() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; vecSizes[1] = 4; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; - baseTypes[1] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3, 4); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x3, vecSizes, baseTypes); final ContextState cs = new ContextState(); @@ -253,10 +261,8 @@ class PipelineCacheVertexInputTest { @Test void denseLayout_locations0_1_2_noPlaceholders() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; vecSizes[1] = 4; vecSizes[2] = 2; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; baseTypes[1] = Spvc.SPVC_BASETYPE_FP32; baseTypes[2] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3, 4, 2); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x7, vecSizes, baseTypes); final ContextState cs = new ContextState(); @@ -277,10 +283,8 @@ class PipelineCacheVertexInputTest { @Test void shaderExpectsButVaoMissing_emitsConstantBinding() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; vecSizes[1] = 4; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; baseTypes[1] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3, 4); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x3, vecSizes, baseTypes); final ContextState cs = new ContextState(); @@ -301,10 +305,8 @@ class PipelineCacheVertexInputTest { @Test void constantBindingUsesZeroPitchExceptOnMetal() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[4] = 3; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[4] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(0, 0, 0, 0, 3); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(1 << 4, vecSizes, baseTypes); final ContextState cs = new ContextState(); @@ -321,10 +323,8 @@ class PipelineCacheVertexInputTest { @Test void vaoExtraAttribNotInShader_droppedFromPipeline() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 4; vecSizes[2] = 4; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; baseTypes[2] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(4, 0, 4); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x5, vecSizes, baseTypes); // shaderInputMask = 0x5 final ContextState cs = new ContextState(); @@ -359,10 +359,8 @@ class PipelineCacheVertexInputTest { @Test void interiorGapWithoutExpansion_fillsGapWithPlaceholder() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; vecSizes[3] = 2; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; baseTypes[3] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3, 0, 0, 2); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x9, vecSizes, baseTypes); final ContextState cs = new ContextState(); @@ -393,10 +391,8 @@ class PipelineCacheVertexInputTest { @Test void enabledAttribWithoutPointer_treatedAsDisabled() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; vecSizes[1] = 4; vecSizes[3] = 2; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; baseTypes[1] = Spvc.SPVC_BASETYPE_FP32; baseTypes[3] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3, 4, 0, 2); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0xB, vecSizes, baseTypes); // locs 0, 1, 3 final ContextState cs = new ContextState(); @@ -446,10 +442,8 @@ class PipelineCacheVertexInputTest { @Test void enabledAttribWithDeadBuffer_demotedToConstantExpansion() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; vecSizes[1] = 4; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; baseTypes[1] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3, 4); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x3, vecSizes, baseTypes); store.setBufferHandleResolver(id -> id == 42 ? 0xBEEFL : 0L); @@ -472,10 +466,8 @@ class PipelineCacheVertexInputTest { @Test void pipelineKeyDiffersBetweenDeadAndLiveBinding() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; vecSizes[1] = 4; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; baseTypes[1] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3, 4); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x3, vecSizes, baseTypes); final ContextState cs = new ContextState(); @@ -505,10 +497,8 @@ class PipelineCacheVertexInputTest { @Test void vertexInputSetIdChangeInvalidatesInputHash() { - final int[] vecSizesA = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizesA[0] = 3; - final int[] baseTypesA = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypesA[0] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizesA = vecSizes(3); + final int[] baseTypesA = fp32(vecSizesA); final String[] namesA = new String[ContextState.MAX_VERTEX_ATTRIBS]; namesA[0] = "a_Pos"; final PipelineCache c = cache(1, 0x1, vecSizesA, baseTypesA); @@ -528,10 +518,8 @@ class PipelineCacheVertexInputTest { final boolean dirtyAfterSameIdDistinctArrays = Reflect.get(c, "inputDirty"); assertFalse(dirtyAfterSameIdDistinctArrays, "the same interned id must not force a rehash even with distinct array instances"); - final int[] vecSizesB = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizesB[0] = 3; vecSizesB[1] = 4; - final int[] baseTypesB = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypesB[0] = Spvc.SPVC_BASETYPE_FP32; baseTypesB[1] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizesB = vecSizes(3, 4); + final int[] baseTypesB = fp32(vecSizesB); final String[] namesB = new String[ContextState.MAX_VERTEX_ATTRIBS]; namesB[0] = "a_Pos"; namesB[1] = "a_Extra"; @@ -546,10 +534,8 @@ class PipelineCacheVertexInputTest { @Test void bindingsArrayIsDenseIndexedBySlot() { // D3D12 requirement: SDL's D3D12 backend reads vertex_buffer_descriptions[buffer_slot] as a flat array, so bindings.get(i).slot() must equal i - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; vecSizes[5] = 2; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; baseTypes[5] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3, 0, 0, 0, 0, 2); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x21, vecSizes, baseTypes); final ContextState cs = new ContextState(); @@ -568,10 +554,8 @@ class PipelineCacheVertexInputTest { @Test void offsetOnlyPointerChange_keepsKeyAndSkipsRecompute() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x1, vecSizes, baseTypes); final ContextState cs = new ContextState(); @@ -590,10 +574,8 @@ class PipelineCacheVertexInputTest { @Test void identicalReissuedPointer_isNoOp() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x1, vecSizes, baseTypes); final ContextState cs = new ContextState(); @@ -612,10 +594,8 @@ class PipelineCacheVertexInputTest { @Test void twoEqualLayoutVaosShareKey() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; vecSizes[1] = 4; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; baseTypes[1] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3, 4); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x3, vecSizes, baseTypes); final ContextState csA = new ContextState(); @@ -631,10 +611,8 @@ class PipelineCacheVertexInputTest { @Test void everyLayoutFieldChangesKey() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x1, vecSizes, baseTypes); final ContextState base = new ContextState(); @@ -692,10 +670,8 @@ class PipelineCacheVertexInputTest { @Test void livenessGenBumpForcesRecompute() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(0x1, vecSizes, baseTypes); final ContextState cs = new ContextState(); @@ -714,14 +690,10 @@ class PipelineCacheVertexInputTest { @Test void alternatingTwoInputSetsOnOneVao_loopRunsExactlyTwice() { - final int[] vecSizesA = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizesA[0] = 3; - final int[] baseTypesA = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypesA[0] = Spvc.SPVC_BASETYPE_FP32; - final int[] vecSizesB = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizesB[0] = 3; vecSizesB[1] = 4; - final int[] baseTypesB = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypesB[0] = Spvc.SPVC_BASETYPE_FP32; baseTypesB[1] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizesA = vecSizes(3); + final int[] baseTypesA = fp32(vecSizesA); + final int[] vecSizesB = vecSizes(3, 4); + final int[] baseTypesB = fp32(vecSizesB); final String[] names = new String[ContextState.MAX_VERTEX_ATTRIBS]; final PipelineCache c = new PipelineCache(); @@ -745,10 +717,8 @@ class PipelineCacheVertexInputTest { @Test void togglingOneAttribEnable_loopRunsExactlyTwice() { - final int[] vecSizes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizes[0] = 3; vecSizes[1] = 4; - final int[] baseTypes = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypes[0] = Spvc.SPVC_BASETYPE_FP32; baseTypes[1] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizes = vecSizes(3, 4); + final int[] baseTypes = fp32(vecSizes); final PipelineCache c = cache(1, 0x3, vecSizes, baseTypes); final ContextState cs = new ContextState(); @@ -808,19 +778,15 @@ class PipelineCacheVertexInputTest { @Test void internVertexInputSet_equalSetsShareId_differentSetsDiffer() { final ShaderManager sm = new ShaderManager(null); - final int[] vecSizesA = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizesA[0] = 3; - final int[] baseTypesA = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypesA[0] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizesA = vecSizes(3); + final int[] baseTypesA = fp32(vecSizesA); final int idA1 = Reflect.invoke(sm, "internVertexInputSet", new Class[] { int.class, int[].class, int[].class }, 0x1, vecSizesA, baseTypesA); final int idA2 = Reflect.invoke(sm, "internVertexInputSet", new Class[] { int.class, int[].class, int[].class }, 0x1, vecSizesA.clone(), baseTypesA.clone()); assertEquals(idA1, idA2, "two equal input sets, even from distinct array instances, must intern to the same id"); - final int[] vecSizesB = new int[ContextState.MAX_VERTEX_ATTRIBS]; - vecSizesB[0] = 3; vecSizesB[1] = 4; - final int[] baseTypesB = new int[ContextState.MAX_VERTEX_ATTRIBS]; - baseTypesB[0] = Spvc.SPVC_BASETYPE_FP32; baseTypesB[1] = Spvc.SPVC_BASETYPE_FP32; + final int[] vecSizesB = vecSizes(3, 4); + final int[] baseTypesB = fp32(vecSizesB); final int idB = Reflect.invoke(sm, "internVertexInputSet", new Class[] { int.class, int[].class, int[].class }, 0x3, vecSizesB, baseTypesB); assertTrue(idA1 != idB, "different input sets must never share an id"); assertTrue(idA1 >= 1 && idB >= 1, "interned ids start at 1"); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PrimitiveRestartScanTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PrimitiveRestartScanTest.java index b77407d2f..c8dec2bfc 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PrimitiveRestartScanTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/PrimitiveRestartScanTest.java @@ -1,14 +1,16 @@ package com.gtnewhorizons.angelica.sdlgpu.pipeline; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL11; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; class PrimitiveRestartScanTest { @@ -27,11 +29,7 @@ class PrimitiveRestartScanTest { } private static void assertSplits(EBOSplitScanner.EboSplit[] actual, int... pairs) { - final EBOSplitScanner.EboSplit[] expected = new EBOSplitScanner.EboSplit[pairs.length / 2]; - for (int i = 0; i < expected.length; i++) { - expected[i] = new EBOSplitScanner.EboSplit(pairs[i * 2], pairs[i * 2 + 1]); - } - assertArrayEquals(expected, actual); + assertArrayEquals(runs(pairs), actual); } private static EBOSplitScanner.EboSplit[] slice(EBOSplitScanner.EboSplit[] full, int firstIndex, int count) { @@ -43,96 +41,58 @@ class PrimitiveRestartScanTest { return out; } - @Test - void shortBuffer_noSentinel_singleFullRun() { - final ByteBuffer b = shortBuf(0, 1, 2, 3, 4); + private static final int SHORT = GL11.GL_UNSIGNED_SHORT; + private static final int INT = GL11.GL_UNSIGNED_INT; + + private static Arguments scan(String name, int glType, int sentinel, int[] indices, int... expectedPairs) { + return Arguments.of(name, glType, sentinel, indices, expectedPairs); + } + + static Stream scans() { + return Stream.of( + scan("short, no sentinel: single full run", SHORT, 0xFFFF, new int[]{0, 1, 2, 3, 4}, 0, 5), + scan("short, sentinel in the middle: two runs", SHORT, 0xFFFF, new int[]{0, 1, 2, 0xFFFF, 4, 5, 6}, 0, 3, 4, 3), + scan("short, leading sentinel: skips to the first run", SHORT, 0xFFFF, new int[]{0xFFFF, 1, 2, 3}, 1, 3), + scan("short, trailing sentinel: drops the tail", SHORT, 0xFFFF, new int[]{0, 1, 2, 0xFFFF}, 0, 3), + scan("short, consecutive sentinels collapse", SHORT, 0xFFFF, new int[]{0, 1, 0xFFFF, 0xFFFF, 0xFFFF, 5, 6}, 0, 2, 5, 2), + scan("short, all sentinels: no runs", SHORT, 0xFFFF, new int[]{0xFFFF, 0xFFFF, 0xFFFF}), + scan("int, 0xFFFFFFFF sentinel", INT, 0xFFFFFFFF, new int[]{0, 1, 2, 0xFFFFFFFF, 4, 5}, 0, 3, 4, 2), + scan("int, custom sentinel 0", INT, 0, new int[]{5, 6, 0, 7, 8, 0, 9}, 0, 2, 3, 2, 6, 1)); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("scans") + void scanIndicesSplitsAtTheSentinel(String name, int glType, int sentinel, int[] indices, int[] expectedPairs) { + final ByteBuffer b = glType == SHORT ? shortBuf(indices) : intBuf(indices); try { - assertSplits(EBOSplitScanner.scanIndices(b, 5, GL11.GL_UNSIGNED_SHORT, 0xFFFF), 0, 5); - } finally { MemoryUtil.memFree(b); } + assertSplits(EBOSplitScanner.scanIndices(b, indices.length, glType, sentinel), expectedPairs); + } finally { + MemoryUtil.memFree(b); + } } - @Test - void shortBuffer_sentinelMiddle_twoRuns() { - final ByteBuffer b = shortBuf(0, 1, 2, 0xFFFF, 4, 5, 6); - try { - assertSplits(EBOSplitScanner.scanIndices(b, 7, GL11.GL_UNSIGNED_SHORT, 0xFFFF), 0, 3, 4, 3); - } finally { MemoryUtil.memFree(b); } + private static EBOSplitScanner.EboSplit[] runs(int... pairs) { + final EBOSplitScanner.EboSplit[] out = new EBOSplitScanner.EboSplit[pairs.length / 2]; + for (int i = 0; i < out.length; i++) out[i] = new EBOSplitScanner.EboSplit(pairs[i * 2], pairs[i * 2 + 1]); + return out; } - @Test - void shortBuffer_sentinelLeading_skipsToFirstRun() { - final ByteBuffer b = shortBuf(0xFFFF, 1, 2, 3); - try { - assertSplits(EBOSplitScanner.scanIndices(b, 4, GL11.GL_UNSIGNED_SHORT, 0xFFFF), 1, 3); - } finally { MemoryUtil.memFree(b); } + private static Arguments window(String name, EBOSplitScanner.EboSplit[] full, int firstIndex, int count, int... expectedPairs) { + return Arguments.of(name, full, firstIndex, count, expectedPairs); } - @Test - void shortBuffer_sentinelTrailing_dropsTail() { - final ByteBuffer b = shortBuf(0, 1, 2, 0xFFFF); - try { - assertSplits(EBOSplitScanner.scanIndices(b, 4, GL11.GL_UNSIGNED_SHORT, 0xFFFF), 0, 3); - } finally { MemoryUtil.memFree(b); } + static Stream windows() { + return Stream.of( + window("no runs", runs(), 0, 10), + window("window contains one run: translated to the window", runs(0, 3, 4, 3), 4, 3, 0, 3), + window("window spans several runs", runs(0, 3, 4, 3, 8, 2), 2, 7, 0, 1, 2, 3, 6, 1), + window("window outside every run", runs(0, 3, 4, 3), 100, 50), + window("window clips a run's start and end", runs(2, 10), 5, 4, 0, 4)); } - @Test - void shortBuffer_consecutiveSentinels_collapse() { - final ByteBuffer b = shortBuf(0, 1, 0xFFFF, 0xFFFF, 0xFFFF, 5, 6); - try { - assertSplits(EBOSplitScanner.scanIndices(b, 7, GL11.GL_UNSIGNED_SHORT, 0xFFFF), 0, 2, 5, 2); - } finally { MemoryUtil.memFree(b); } - } - - @Test - void shortBuffer_allSentinels_emptyOutput() { - final ByteBuffer b = shortBuf(0xFFFF, 0xFFFF, 0xFFFF); - try { - assertEquals(0, EBOSplitScanner.scanIndices(b, 3, GL11.GL_UNSIGNED_SHORT, 0xFFFF).length); - } finally { MemoryUtil.memFree(b); } - } - - @Test - void intBuffer_largeSentinel() { - final ByteBuffer b = intBuf(0, 1, 2, 0xFFFFFFFF, 4, 5); - try { - assertSplits(EBOSplitScanner.scanIndices(b, 6, GL11.GL_UNSIGNED_INT, 0xFFFFFFFF), 0, 3, 4, 2); - } finally { MemoryUtil.memFree(b); } - } - - @Test - void intBuffer_customSentinelZero() { - final ByteBuffer b = intBuf(5, 6, 0, 7, 8, 0, 9); - try { - assertSplits(EBOSplitScanner.scanIndices(b, 7, GL11.GL_UNSIGNED_INT, 0), 0, 2, 3, 2, 6, 1); - } finally { MemoryUtil.memFree(b); } - } - - @Test - void sliceSplits_emptyFull_returnsEmpty() { - assertEquals(0, slice(new EBOSplitScanner.EboSplit[0], 0, 10).length); - } - - @Test - void sliceSplits_windowContainsOneRun_returnsTranslated() { - final EBOSplitScanner.EboSplit[] full = { new EBOSplitScanner.EboSplit(0, 3), new EBOSplitScanner.EboSplit(4, 3) }; - assertSplits(slice(full, 4, 3), 0, 3); - } - - @Test - void sliceSplits_windowSpansMultipleRuns() { - final EBOSplitScanner.EboSplit[] full = { new EBOSplitScanner.EboSplit(0, 3), new EBOSplitScanner.EboSplit(4, 3), new EBOSplitScanner.EboSplit(8, 2) }; - assertSplits(slice(full, 2, 7), 0, 1, 2, 3, 6, 1); - } - - @Test - void sliceSplits_windowOutsideAllRuns_returnsEmpty() { - final EBOSplitScanner.EboSplit[] full = { new EBOSplitScanner.EboSplit(0, 3), new EBOSplitScanner.EboSplit(4, 3) }; - assertEquals(0, slice(full, 100, 50).length); - } - - @Test - void sliceSplits_windowClipsRunStartAndEnd() { - final EBOSplitScanner.EboSplit[] full = { new EBOSplitScanner.EboSplit(2, 10) }; // [2, 12) - assertSplits(slice(full, 5, 4), 0, 4); + @ParameterizedTest(name = "{0}") + @MethodSource("windows") + void sliceSplitsClipsRunsToTheWindow(String name, EBOSplitScanner.EboSplit[] full, int firstIndex, int count, int[] expectedPairs) { + assertSplits(slice(full, firstIndex, count), expectedPairs); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/ScissorClampTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/ScissorClampTest.java index 74f5a58ec..38c432159 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/ScissorClampTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/ScissorClampTest.java @@ -1,68 +1,42 @@ package com.gtnewhorizons.angelica.sdlgpu.pipeline; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; class ScissorClampTest { - private final int[] out = new int[4]; - - @Test - void rectInsideTheTargetIsUnchanged() { - assertTrue(ScissorClamp.clamp(4, 8, 16, 32, 64, 96, out)); - assertArrayEquals(new int[]{4, 8, 16, 32}, out); + private static Arguments rect(String name, int x, int y, int w, int h, int targetW, int targetH, boolean nonEmpty, int... expectedOut) { + return Arguments.of(name, x, y, w, h, targetW, targetH, nonEmpty, expectedOut); } - @Test - void overhangPastTheRightAndBottomEdgesShrinks() { - assertTrue(ScissorClamp.clamp(0, 0, 1710, 960, 1708, 960, out)); - assertArrayEquals(new int[]{0, 0, 1708, 960}, out); - - assertTrue(ScissorClamp.clamp(60, 90, 16, 32, 64, 96, out)); - assertArrayEquals(new int[]{60, 90, 4, 6}, out); + /** No expected rect: only the return value is specified for that input. */ + static Stream rects() { + return Stream.of( + rect("inside the target: unchanged", 4, 8, 16, 32, 64, 96, true, 4, 8, 16, 32), + rect("overhang past the right edge shrinks", 0, 0, 1710, 960, 1708, 960, true, 0, 0, 1708, 960), + rect("overhang past the right and bottom edges shrinks", 60, 90, 16, 32, 64, 96, true, 60, 90, 4, 6), + rect("negative origin shrinks instead of shifting", -6, -10, 16, 32, 64, 96, true, 0, 0, 10, 22), + rect("straddling both edges clamps to the whole target", -8, -8, 128, 128, 64, 96, true, 0, 0, 64, 96), + rect("fully right of the target: empty", 64, 0, 16, 32, 64, 96, false, 64, 0, 0, 32), + rect("fully left of the target: empty", -40, 0, 16, 32, 64, 96, false, 0, 0, 0, 32), + rect("zero-width source: empty", 0, 0, 0, 32, 64, 96, false), + rect("negative-height source: empty", 0, 0, 16, -4, 64, 96, false), + rect("unknown target size leaves the rect alone", -6, 4, 1710, 32, 0, 0, true, -6, 4, 1710, 32), + rect("unknown target width with a zero-width source: empty", 0, 0, 0, 32, 0, 96, false), + rect("large rect does not overflow", 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE, 64, 96, true, 0, 0, 64, 96)); } - @Test - void negativeOriginShrinksTheRectInsteadOfShiftingIt() { - assertTrue(ScissorClamp.clamp(-6, -10, 16, 32, 64, 96, out)); - assertArrayEquals(new int[]{0, 0, 10, 22}, out); - } - - @Test - void rectStraddlingBothEdgesIsClampedToTheWholeTarget() { - assertTrue(ScissorClamp.clamp(-8, -8, 128, 128, 64, 96, out)); - assertArrayEquals(new int[]{0, 0, 64, 96}, out); - } - - @Test - void rectFullyOutsideTheTargetIsEmpty() { - assertFalse(ScissorClamp.clamp(64, 0, 16, 32, 64, 96, out)); - assertArrayEquals(new int[]{64, 0, 0, 32}, out); - - assertFalse(ScissorClamp.clamp(-40, 0, 16, 32, 64, 96, out)); - assertArrayEquals(new int[]{0, 0, 0, 32}, out); - } - - @Test - void degenerateSourceRectIsEmpty() { - assertFalse(ScissorClamp.clamp(0, 0, 0, 32, 64, 96, out)); - assertFalse(ScissorClamp.clamp(0, 0, 16, -4, 64, 96, out)); - } - - @Test - void unknownTargetSizeLeavesTheRectAlone() { - assertTrue(ScissorClamp.clamp(-6, 4, 1710, 32, 0, 0, out)); - assertArrayEquals(new int[]{-6, 4, 1710, 32}, out); - - assertFalse(ScissorClamp.clamp(0, 0, 0, 32, 0, 96, out)); - } - - @Test - void largeRectDoesNotOverflow() { - assertTrue(ScissorClamp.clamp(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE, 64, 96, out)); - assertArrayEquals(new int[]{0, 0, 64, 96}, out); + @ParameterizedTest(name = "{0}") + @MethodSource("rects") + void clamp(String name, int x, int y, int w, int h, int targetW, int targetH, boolean nonEmpty, int[] expectedOut) { + final int[] out = new int[4]; + assertEquals(nonEmpty, ScissorClamp.clamp(x, y, w, h, targetW, targetH, out)); + if (expectedOut.length != 0) assertArrayEquals(expectedOut, out); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/StencilMaskGpuTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/StencilMaskGpuTest.java index d1b377c4e..462c806fc 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/StencilMaskGpuTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/pipeline/StencilMaskGpuTest.java @@ -1,15 +1,13 @@ package com.gtnewhorizons.angelica.sdlgpu.pipeline; +import com.gtnewhorizons.angelica.sdlgpu.RealDeviceExtension; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; -import com.gtnewhorizons.angelica.sdlgpu.resource.PixelOps; import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.lwjgl.opengl.GL11; +import org.junit.jupiter.api.extension.ExtendWith; import org.lwjgl.opengl.GL20; -import org.lwjgl.opengl.GL30; import org.lwjgl.sdl.SDLError; import org.lwjgl.sdl.SDL_GPUColorTargetInfo; import org.lwjgl.sdl.SDL_GPUDepthStencilTargetInfo; @@ -22,6 +20,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.lwjgl.sdl.SDLGPU.*; +@ExtendWith(RealDeviceExtension.class) class StencilMaskGpuTest { private static SdlTestRig rig; @@ -65,31 +64,23 @@ class StencilMaskGpuTest { @BeforeAll static void setUp() throws Exception { - rig = SdlTestRig.acquireRealDevice(); - rig.resourceManager.cachePreferredDepthFormats(); + rig = RealDeviceExtension.rig(); sm = new ShaderManager(rig.device); store = new PipelineStore(rig.device); - colorTexture = rig.resourceManager.createTexture(9500, GL11.GL_TEXTURE_2D, GL11.GL_RGBA8, SIZE, SIZE, 1, 1); - assertTrue(colorTexture != 0, "color target creation failed"); - colorFormat = rig.resourceManager.getTextureMeta(9500).sdlFormat(); + final RealDeviceExtension.Target color = RealDeviceExtension.colorTarget(9500, SIZE, SIZE); + colorTexture = color.texture(); + colorFormat = color.sdlFormat(); PipelineCache.setSwapchainFormats(new int[]{ colorFormat }); - rig.resourceManager.createTexture(9501, GL11.GL_TEXTURE_2D, GL30.GL_DEPTH24_STENCIL8, SIZE, SIZE, 1, 1); - depthTexture = rig.resourceManager.ensureTextureUsage(9501, SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET); - assertTrue(depthTexture != 0, "depth+stencil target creation failed"); - depthFormat = rig.resourceManager.getTextureMeta(9501).sdlFormat(); - assertEquals(8, PixelOps.stencilBits(depthFormat), "test needs a packed depth+stencil format"); + final RealDeviceExtension.Target depth = RealDeviceExtension.depthStencilTarget(9501, SIZE, SIZE); + depthTexture = depth.texture(); + depthFormat = depth.sdlFormat(); maskPipeline = createPipeline(MASK_FRAGMENT, true); fillPipeline = createPipeline(FILL_FRAGMENT, false); } - @AfterAll - static void tearDown() { - SdlTestRig.releaseRealDevice(); - } - private static long createPipeline(String fragmentSource, boolean writesMask) { final int vs = sm.createShader(GL20.GL_VERTEX_SHADER); sm.shaderSource(vs, VERTEX); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BgraStorageChannelTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BgraStorageChannelTest.java index 990962185..1c1bf92d4 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BgraStorageChannelTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BgraStorageChannelTest.java @@ -1,12 +1,16 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; import static org.lwjgl.sdl.SDLGPU.*; @@ -55,78 +59,42 @@ class BgraStorageChannelTest { assertArrayEquals(new byte[]{3, 2, 1, 4}, toArray(p, 4)); } - @Test - void upload_rgbaSrc_rgbaStorage_passesThrough() { + static Stream matchingLayouts() { + return Stream.of( + Arguments.of("RGBA source, RGBA storage", GL11.GL_RGBA, RGBA), + Arguments.of("BGRA source, BGRA storage", GL12.GL_BGRA, BGRA)); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("matchingLayouts") + void upload_matchingChannelOrder_passesThrough(String name, int glFormat, int sdlStorage) { final ByteBuffer src = pixel(0x11, 0x22, 0x33, 0x44); - final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, RGBA, src, 1, 1); - assertSame(src, out, "no transform expected"); + assertSame(src, PixelOps.prepareUploadBuffer(glFormat, GL11.GL_UNSIGNED_BYTE, sdlStorage, src, 1, 1), "no transform expected"); } - @Test - void upload_bgraSrc_bgraStorage_passesThrough() { - final ByteBuffer src = pixel(0x11, 0x22, 0x33, 0x44); - final ByteBuffer out = PixelOps.prepareUploadBuffer(GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, BGRA, src, 1, 1); - assertSame(src, out); + private static Arguments converted(String name, int glFormat, int sdlStorage, byte[] src, byte... expected) { + return Arguments.of(name, glFormat, sdlStorage, src, expected); } - @Test - void upload_rgbaSrc_bgraStorage_swaps() { - final ByteBuffer src = pixel(0x11, 0x22, 0x33, 0x44); - final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, BGRA, src, 1, 1); + static Stream convertedUploads() { + final byte[] rgba = {0x11, 0x22, 0x33, 0x44}; + return Stream.of( + converted("RGBA source, BGRA storage: swaps", GL11.GL_RGBA, BGRA, rgba, (byte) 0x33, (byte) 0x22, (byte) 0x11, (byte) 0x44), + converted("BGRA source, RGBA storage: swaps", GL12.GL_BGRA, RGBA, rgba, (byte) 0x33, (byte) 0x22, (byte) 0x11, (byte) 0x44), + converted("luminance source, RGBA storage: expands", GL11.GL_LUMINANCE, RGBA, new byte[]{0x77}, (byte) 0x77, (byte) 0x77, (byte) 0x77, (byte) 0xFF), + converted("luminance source, BGRA storage: expands and swaps", GL11.GL_LUMINANCE, BGRA, new byte[]{0x77}, (byte) 0x77, (byte) 0x77, (byte) 0x77, (byte) 0xFF), + converted("luminance-alpha source, BGRA storage: expands and swaps", GL11.GL_LUMINANCE_ALPHA, BGRA, new byte[]{0x42, (byte) 0x88}, (byte) 0x42, (byte) 0x42, (byte) 0x42, (byte) 0x88)); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("convertedUploads") + void upload_convertsIntoANewBuffer(String name, int glFormat, int sdlStorage, byte[] srcBytes, byte[] expected) { + final ByteBuffer src = ByteBuffer.allocateDirect(srcBytes.length).order(ByteOrder.nativeOrder()); + src.put(srcBytes).rewind(); + final ByteBuffer out = PixelOps.prepareUploadBuffer(glFormat, GL11.GL_UNSIGNED_BYTE, sdlStorage, src, 1, 1); try { assertNotSame(src, out); - assertArrayEquals(new byte[]{0x33, 0x22, 0x11, 0x44}, toArray(out, 4)); - } finally { - MemoryUtil.memFree(out); - } - } - - @Test - void upload_bgraSrc_rgbaStorage_swaps() { - final ByteBuffer src = pixel(0x11, 0x22, 0x33, 0x44); - final ByteBuffer out = PixelOps.prepareUploadBuffer(GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, RGBA, src, 1, 1); - try { - assertNotSame(src, out); - assertArrayEquals(new byte[]{0x33, 0x22, 0x11, 0x44}, toArray(out, 4)); - } finally { - MemoryUtil.memFree(out); - } - } - - @Test - void upload_luminanceSrc_rgbaStorage_expandsOnly() { - final ByteBuffer src = ByteBuffer.allocateDirect(1).order(ByteOrder.nativeOrder()); - src.put((byte) 0x77).rewind(); - final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE, RGBA, src, 1, 1); - try { - assertNotSame(src, out); - assertArrayEquals(new byte[]{0x77, 0x77, 0x77, (byte) 0xFF}, toArray(out, 4)); - } finally { - MemoryUtil.memFree(out); - } - } - - @Test - void upload_luminanceSrc_bgraStorage_expandsAndSwaps() { - final ByteBuffer src = ByteBuffer.allocateDirect(1).order(ByteOrder.nativeOrder()); - src.put((byte) 0x77).rewind(); - final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_LUMINANCE, GL11.GL_UNSIGNED_BYTE, BGRA, src, 1, 1); - try { - assertNotSame(src, out); - assertArrayEquals(new byte[]{0x77, 0x77, 0x77, (byte) 0xFF}, toArray(out, 4)); - } finally { - MemoryUtil.memFree(out); - } - } - - @Test - void upload_luminanceAlphaSrc_bgraStorage_expandsAndSwaps() { - final ByteBuffer src = ByteBuffer.allocateDirect(2).order(ByteOrder.nativeOrder()); - src.put((byte) 0x42).put((byte) 0x88).rewind(); - final ByteBuffer out = PixelOps.prepareUploadBuffer(GL11.GL_LUMINANCE_ALPHA, GL11.GL_UNSIGNED_BYTE, BGRA, src, 1, 1); - try { - assertNotSame(src, out); - assertArrayEquals(new byte[]{0x42, 0x42, 0x42, (byte) 0x88}, toArray(out, 4)); + assertArrayEquals(expected, toArray(out, 4)); } finally { MemoryUtil.memFree(out); } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BlitCopyDecisionTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BlitCopyDecisionTest.java index b26bbe867..8ab4b0c30 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BlitCopyDecisionTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BlitCopyDecisionTest.java @@ -1,9 +1,14 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL11; +import java.util.stream.Stream; + import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.lwjgl.sdl.SDLGPU.*; class BlitCopyDecisionTest { @@ -12,29 +17,26 @@ class BlitCopyDecisionTest { return new ResourceManager.TextureMeta(GL11.GL_TEXTURE_2D, GL11.GL_RGBA8, sdlFormat, 1024, 1024, 1, 1, 0); } - @Test - void sameFormatOneToOneCopies() { - assertTrue(TextureOps.canCopyInsteadOfBlit(meta(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM), meta(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM), 1024, 1024, 1024, 1024)); + private static final int RGBA = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; + private static final int BGRA = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM; + + private static Arguments blit(String name, boolean canCopy, ResourceManager.TextureMeta src, ResourceManager.TextureMeta dst, int srcW, int srcH, int dstW, int dstH) { + return Arguments.of(name, canCopy, src, dst, srcW, srcH, dstW, dstH); } - @Test - void formatMismatchBlits() { - assertFalse(TextureOps.canCopyInsteadOfBlit(meta(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM), meta(SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM), 1024, 1024, 1024, 1024)); + static Stream blits() { + return Stream.of( + blit("same format, 1:1: copies", true, meta(RGBA), meta(RGBA), 1024, 1024, 1024, 1024), + blit("format mismatch: blits", false, meta(RGBA), meta(BGRA), 1024, 1024, 1024, 1024), + blit("scaling: blits", false, meta(RGBA), meta(RGBA), 1024, 1024, 512, 512), + blit("flipped region: blits", false, meta(RGBA), meta(RGBA), -1024, 1024, -1024, 1024), + blit("missing source meta: blits", false, null, meta(RGBA), 1024, 1024, 1024, 1024), + blit("missing destination meta: blits", false, meta(RGBA), null, 1024, 1024, 1024, 1024)); } - @Test - void scalingBlits() { - assertFalse(TextureOps.canCopyInsteadOfBlit(meta(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM), meta(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM), 1024, 1024, 512, 512)); - } - - @Test - void flippedRegionBlits() { - assertFalse(TextureOps.canCopyInsteadOfBlit(meta(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM), meta(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM), -1024, 1024, -1024, 1024)); - } - - @Test - void missingMetaBlits() { - assertFalse(TextureOps.canCopyInsteadOfBlit(null, meta(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM), 1024, 1024, 1024, 1024)); - assertFalse(TextureOps.canCopyInsteadOfBlit(meta(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM), null, 1024, 1024, 1024, 1024)); + @ParameterizedTest(name = "{0}") + @MethodSource("blits") + void canCopyInsteadOfBlit(String name, boolean canCopy, ResourceManager.TextureMeta src, ResourceManager.TextureMeta dst, int srcW, int srcH, int dstW, int dstH) { + assertEquals(canCopy, TextureOps.canCopyInsteadOfBlit(src, dst, srcW, srcH, dstW, dstH)); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BufferContentsDefinedTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BufferContentsDefinedTest.java index 4e736e3dd..342c6f173 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BufferContentsDefinedTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BufferContentsDefinedTest.java @@ -8,18 +8,14 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class BufferContentsDefinedTest { - private static ResourceManager newResourceManager() { - return SdlTestRig.resourceManager(); - } - @Test void unknownBuffer_isNotUndefined() { - assertFalse(newResourceManager().hasUndefinedContents(4242)); + assertFalse(SdlTestRig.resourceManager().hasUndefinedContents(4242)); } @Test void markDefined_clearsTheFlag() { - final ResourceManager rm = newResourceManager(); + final ResourceManager rm = SdlTestRig.resourceManager(); SdlReflect.markUndefined(rm, 7); assertTrue(rm.hasUndefinedContents(7)); @@ -29,7 +25,7 @@ class BufferContentsDefinedTest { @Test void markDefined_isIdempotentAndIgnoresZero() { - final ResourceManager rm = newResourceManager(); + final ResourceManager rm = SdlTestRig.resourceManager(); rm.markBufferContentsDefined(0); rm.markBufferContentsDefined(9); rm.markBufferContentsDefined(9); @@ -38,7 +34,7 @@ class BufferContentsDefinedTest { @Test void deleteBuffer_clearsTheFlag() { - final ResourceManager rm = newResourceManager(); + final ResourceManager rm = SdlTestRig.resourceManager(); SdlReflect.markUndefined(rm, 5); rm.recordBufferSizeOnly(5, 1000); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BufferGlParamsTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BufferGlParamsTest.java index 8c61831ff..b91ce5634 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BufferGlParamsTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BufferGlParamsTest.java @@ -2,26 +2,18 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; -import com.gtnewhorizons.angelica.sdlgpu.device.Device; -import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; import org.junit.jupiter.api.Test; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL44; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; class BufferGlParamsTest { - private static ResourceManager newResourceManager() { - return SdlTestRig.resourceManager(); - } - @Test void unknownBuffer_reportsGlDefaults() { - final ResourceManager rm = newResourceManager(); + final ResourceManager rm = SdlTestRig.resourceManager(); assertEquals(GL15.GL_STATIC_DRAW, rm.getBufferGlUsage(4242)); assertEquals(BufferParams.MUTABLE_STORE, rm.getBufferStorageFlags(4242)); assertEquals(0L, rm.getBufferSize(4242)); @@ -29,7 +21,7 @@ class BufferGlParamsTest { @Test void recordedParams_roundTrip() { - final ResourceManager rm = newResourceManager(); + final ResourceManager rm = SdlTestRig.resourceManager(); rm.recordBufferGlParams(1, GL15.GL_STREAM_DRAW, BufferParams.MUTABLE_STORE); assertEquals(GL15.GL_STREAM_DRAW, rm.getBufferGlUsage(1)); assertEquals(BufferParams.MUTABLE_STORE, rm.getBufferStorageFlags(1)); @@ -42,7 +34,7 @@ class BufferGlParamsTest { @Test void recordedParams_overwrittenOnRespecification() { - final ResourceManager rm = newResourceManager(); + final ResourceManager rm = SdlTestRig.resourceManager(); rm.recordBufferGlParams(3, GL15.GL_DYNAMIC_DRAW, GL30.GL_MAP_WRITE_BIT); rm.recordBufferGlParams(3, GL15.GL_STATIC_DRAW, BufferParams.MUTABLE_STORE); assertEquals(GL15.GL_STATIC_DRAW, rm.getBufferGlUsage(3)); @@ -51,7 +43,7 @@ class BufferGlParamsTest { @Test void deleteBuffer_clearsParamsBackToDefaults() { - final ResourceManager rm = newResourceManager(); + final ResourceManager rm = SdlTestRig.resourceManager(); rm.recordBufferSizeOnly(5, 1000); rm.recordBufferGlParams(5, GL15.GL_STREAM_READ, GL30.GL_MAP_READ_BIT); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BufferSizeGpuExactnessTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BufferSizeGpuExactnessTest.java index 35a97cdff..f61f4e991 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BufferSizeGpuExactnessTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/BufferSizeGpuExactnessTest.java @@ -1,25 +1,27 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; +import com.gtnewhorizons.angelica.sdlgpu.RealDeviceExtension; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; import com.gtnewhorizons.angelica.sdlgpu.device.Device; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.lwjgl.opengl.GL15; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +@ExtendWith(RealDeviceExtension.class) class BufferSizeGpuExactnessTest { private static Device device; private static ResourceManager resourceManager; - private static long sdlDevice; @BeforeAll static void setUp() throws Exception { - final SdlTestRig rig = SdlTestRig.acquireRealDevice(); + final SdlTestRig rig = RealDeviceExtension.rig(); device = rig.device; resourceManager = rig.resourceManager; } @@ -27,7 +29,6 @@ class BufferSizeGpuExactnessTest { @AfterAll static void tearDown() { if (resourceManager != null) resourceManager.shutdown(); - SdlTestRig.releaseRealDevice(); } @Test diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ClearTexImageFormatTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ClearTexImageFormatTest.java index 7dd4d2218..cb9c83e9d 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ClearTexImageFormatTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ClearTexImageFormatTest.java @@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.lwjgl.sdl.SDLGPU.*; @@ -27,11 +28,7 @@ class ClearTexImageFormatTest { } @Test - void volumeSizingAccountsForDepth() { - final int bpp = PixelOps.sdlFormatTexelBytes(SDL_GPU_TEXTUREFORMAT_R16_UINT); - assertTrue(bpp == 2, "R16_UINT is two bytes per texel, got " + bpp); - final long slice = 512L * 256L * bpp; - final long volume = slice * 512L; - assertTrue(volume / slice == 512, "a depth-aware clear must cover every slice"); + void r16UintIsTwoBytesPerTexel() { + assertEquals(2, PixelOps.sdlFormatTexelBytes(SDL_GPU_TEXTUREFORMAT_R16_UINT)); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ColorContentLifetimeTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ColorContentLifetimeTest.java index d47e8f06b..68ab319b9 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ColorContentLifetimeTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ColorContentLifetimeTest.java @@ -17,12 +17,11 @@ class ColorContentLifetimeTest { } @Test - void definedContentsSurviveAcrossFrames() { + void markedTextureHasDefinedContents() { final ResourceManager rm = SdlTestRig.resourceManager(); rm.markTextureContentDefined(TEX); assertTrue(rm.isTextureContentDefined(TEX)); - assertTrue(rm.isTextureContentDefined(TEX), "contents outlive the frame that produced them"); } @Test diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CopyAllMipsVolumeTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CopyAllMipsVolumeTest.java deleted file mode 100644 index be07a4dbe..000000000 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CopyAllMipsVolumeTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.gtnewhorizons.angelica.sdlgpu.resource; - -import org.junit.jupiter.api.Test; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL12; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class CopyAllMipsVolumeTest { - - private record Copy(int level, int layer, int w, int h, int d) {} - - private static java.util.List plan(int glTarget, int width, int height, int depth, int levels) { - final java.util.List out = new java.util.ArrayList<>(); - final boolean volume = glTarget == GL12.GL_TEXTURE_3D; - final int layers = volume ? 1 : Math.max(1, depth); - for (int level = 0; level < Math.max(1, levels); level++) { - final int w = Math.max(1, width >> level); - final int h = Math.max(1, height >> level); - final int d = volume ? Math.max(1, depth >> level) : 1; - for (int layer = 0; layer < layers; layer++) out.add(new Copy(level, layer, w, h, d)); - } - return out; - } - - @Test - void volumeTextureIsCopiedAsOneRegionPerLevel() { - final var copies = plan(GL12.GL_TEXTURE_3D, 512, 256, 512, 1); - assertEquals(1, copies.size(), "a 3D texture is one copy per mip, not one per depth slice"); - assertEquals(0, copies.get(0).layer(), "slice must stay 0; Metal rejects anything else on a 3D texture"); - assertEquals(512, copies.get(0).d(), "the whole depth must be copied"); - } - - @Test - void arrayTextureStillCopiesPerLayer() { - final var copies = plan(GL11.GL_TEXTURE_2D, 64, 64, 6, 1); - assertEquals(6, copies.size(), "non-volume targets keep per-layer copies"); - for (Copy c : copies) assertEquals(1, c.d(), "layered copies stay one slice deep"); - } - - @Test - void volumeDepthShrinksWithMipLevel() { - final var copies = plan(GL12.GL_TEXTURE_3D, 512, 256, 512, 3); - assertEquals(3, copies.size()); - assertEquals(512, copies.get(0).d()); - assertEquals(256, copies.get(1).d()); - assertEquals(128, copies.get(2).d()); - } -} diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CopyMappedFromDataBoundsTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CopyMappedFromDataBoundsTest.java index 8bde957af..d6a7e8e1e 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CopyMappedFromDataBoundsTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CopyMappedFromDataBoundsTest.java @@ -1,48 +1,35 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; +import java.util.stream.Stream; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class CopyMappedFromDataBoundsTest { - @Test - void throws_whenSizeExceedsDataRemaining() { - final ByteBuffer mapped = MemoryUtil.memAlloc(64); - final ByteBuffer data = MemoryUtil.memAlloc(32); - try { - assertThrows(IllegalStateException.class, - () -> ResourceManager.copyMappedFromData(mapped, data, 64, 1)); - } finally { - MemoryUtil.memFree(mapped); - MemoryUtil.memFree(data); - } + static Stream outOfBounds() { + return Stream.of( + Arguments.of("size exceeds data remaining", 64, 0, 32, 64), + Arguments.of("size exceeds mapped capacity from its position", 64, 48, 128, 32), + Arguments.of("negative size", 16, 0, 16, -1)); } - @Test - void throws_whenSizeExceedsMappedCapacityFromPosition() { - final ByteBuffer mapped = MemoryUtil.memAlloc(64); - final ByteBuffer data = MemoryUtil.memAlloc(128); - mapped.position(48); + @ParameterizedTest(name = "{0}") + @MethodSource("outOfBounds") + void throws_whenOutOfBounds(String name, int mappedCapacity, int mappedPosition, int dataCapacity, int size) { + final ByteBuffer mapped = MemoryUtil.memAlloc(mappedCapacity); + final ByteBuffer data = MemoryUtil.memAlloc(dataCapacity); + mapped.position(mappedPosition); try { assertThrows(IllegalStateException.class, - () -> ResourceManager.copyMappedFromData(mapped, data, 32, 1)); - } finally { - MemoryUtil.memFree(mapped); - MemoryUtil.memFree(data); - } - } - - @Test - void throws_onNegativeSize() { - final ByteBuffer mapped = MemoryUtil.memAlloc(16); - final ByteBuffer data = MemoryUtil.memAlloc(16); - try { - assertThrows(IllegalStateException.class, - () -> ResourceManager.copyMappedFromData(mapped, data, -1, 1)); + () -> ResourceManager.copyMappedFromData(mapped, data, size, 1)); } finally { MemoryUtil.memFree(mapped); MemoryUtil.memFree(data); @@ -57,9 +44,7 @@ class CopyMappedFromDataBoundsTest { try { ResourceManager.copyMappedFromData(mapped, data, 32, 1); for (int i = 0; i < 32; i++) { - if (mapped.get(i) != (byte) (i + 1)) { - throw new AssertionError("byte " + i + " mismatch"); - } + assertEquals((byte) (i + 1), mapped.get(i), "byte " + i); } } finally { MemoryUtil.memFree(mapped); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CopyTexSubImageFromFbo0GpuTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CopyTexSubImageFromFbo0GpuTest.java index 4effab803..a48be8a32 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CopyTexSubImageFromFbo0GpuTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CopyTexSubImageFromFbo0GpuTest.java @@ -1,5 +1,6 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; +import com.gtnewhorizons.angelica.sdlgpu.RealDeviceExtension; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; import com.gtnewhorizons.angelica.sdlgpu.device.Device; import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; @@ -8,6 +9,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.extension.ExtendWith; import org.lwjgl.opengl.GL11; import org.lwjgl.system.MemoryUtil; @@ -17,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; +@ExtendWith(RealDeviceExtension.class) @Timeout(120) class CopyTexSubImageFromFbo0GpuTest { @@ -32,7 +35,7 @@ class CopyTexSubImageFromFbo0GpuTest { @BeforeAll static void setUp() throws Exception { - final SdlTestRig rig = SdlTestRig.acquireRealDevice(); + final SdlTestRig rig = RealDeviceExtension.rig(); device = rig.device; frameManager = rig.frameManager; resourceManager = rig.resourceManager; @@ -43,7 +46,6 @@ class CopyTexSubImageFromFbo0GpuTest { static void tearDown() { if (frameManager != null && frameManager.isFrameActive()) frameManager.endFrame(); if (frameManager != null) frameManager.destroyFinalTarget(); - SdlTestRig.releaseRealDevice(); } private static byte srcRed(int col) { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CrossThreadUploadCBIsolationTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CrossThreadUploadCBIsolationTest.java index 9df20040c..668090c9c 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CrossThreadUploadCBIsolationTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/CrossThreadUploadCBIsolationTest.java @@ -1,110 +1,64 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; +import com.gtnewhorizons.angelica.glsm.testutil.TestThreads; import java.util.concurrent.atomic.AtomicReference; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; - -import com.gtnewhorizons.angelica.sdlgpu.device.Device; import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; import org.junit.jupiter.api.Test; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicLong; - import static com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager.MAX_PENDING_UPLOAD_BYTES; import static org.junit.jupiter.api.Assertions.*; class CrossThreadUploadCBIsolationTest { - private static FrameManager newFM() { - return SdlTestRig.frameManager(); - } - @Test void differentThreadsHaveDistinctFrameStates() throws Exception { - final FrameManager fm = newFM(); + final FrameManager fm = SdlTestRig.frameManager(); final FrameManager.FrameState testThreadFs = fm.frame(); - final AtomicReference workerFs = - new AtomicReference<>(); - final Thread t = new Thread(() -> workerFs.set(fm.frame()), "worker-thread"); - t.start(); - t.join(); + + final AtomicReference workerFs = new AtomicReference<>(); + TestThreads.run("worker-thread", () -> workerFs.set(fm.frame())); + assertNotNull(workerFs.get()); assertNotSame(testThreadFs, workerFs.get(), "each thread must get its own FrameManager.FrameState instance from the FrameManager ThreadLocal"); } @Test void clientThreadsRecordingsDoNotAppearInSplashThreadsState() throws Exception { - final FrameManager fm = newFM(); + final FrameManager fm = SdlTestRig.frameManager(); - final CountDownLatch clientRecorded = new CountDownLatch(1); - final AtomicLong clientBytes = new AtomicLong(); - final AtomicBoolean clientShouldFlush = - new AtomicBoolean(); + TestThreads.run("client-thread-stand-in", () -> { + fm.recordUploadCommands(MAX_PENDING_UPLOAD_BYTES + 64, 200); + assertTrue(fm.frame().pendingUploadBytes > MAX_PENDING_UPLOAD_BYTES); + assertTrue(fm.shouldAutoSubmitPendingUpload()); + }); - final Thread client = new Thread(() -> { - try { - fm.recordUploadCommands(MAX_PENDING_UPLOAD_BYTES + 64, 200); - clientBytes.set(fm.frame().pendingUploadBytes); - clientShouldFlush.set(fm.shouldAutoSubmitPendingUpload()); - } finally { - clientRecorded.countDown(); - } - }, "client-thread-stand-in"); - client.start(); - clientRecorded.await(); - - final AtomicLong splashBytes = new AtomicLong(); - final AtomicInteger splashCommands = - new AtomicInteger(); - final AtomicBoolean splashShouldFlush = - new AtomicBoolean(); - final Thread splash = new Thread(() -> { - splashBytes.set(fm.frame().pendingUploadBytes); - splashCommands.set(fm.frame().pendingUploadCommands); - splashShouldFlush.set(fm.shouldAutoSubmitPendingUpload()); - }, "splash-thread-stand-in"); - splash.start(); - splash.join(); - - assertTrue(clientBytes.get() > MAX_PENDING_UPLOAD_BYTES); - assertTrue(clientShouldFlush.get()); - - assertEquals(0L, splashBytes.get(), "splash thread's pendingUploadBytes must be 0 even though client recorded over threshold"); - assertEquals(0, splashCommands.get()); - assertFalse(splashShouldFlush.get(), "splash thread's shouldAutoSubmitPendingUpload must be false"); + TestThreads.run("splash-thread-stand-in", () -> { + assertEquals(0L, fm.frame().pendingUploadBytes, "splash thread's pendingUploadBytes must be 0 even though client recorded over threshold"); + assertEquals(0, fm.frame().pendingUploadCommands); + assertFalse(fm.shouldAutoSubmitPendingUpload(), "splash thread's shouldAutoSubmitPendingUpload must be false"); + }); } @Test void perThreadFrameStateBatchFieldsAreIndependent() throws Exception { - final FrameManager fm = newFM(); + final FrameManager fm = SdlTestRig.frameManager(); - final CountDownLatch workerSeeded = new CountDownLatch(1); - final AtomicInteger workerListSize = - new AtomicInteger(); - final Thread worker = new Thread(() -> { - try { - final FrameManager.FrameState f = fm.frame(); - f.pendingTexHandles.add(1L); - f.pendingTexX.add(0); - f.pendingTexY.add(0); - f.pendingTexW.add(16); - f.pendingTexH.add(16); - f.pendingTexLevels.add(0); - f.pendingTexOffsets.add(0); - f.batchOffset = 1024; - workerListSize.set(f.pendingTexHandles.size()); - } finally { - workerSeeded.countDown(); - } - }, "worker"); - worker.start(); - workerSeeded.await(); + TestThreads.run("worker", () -> { + final FrameManager.FrameState f = fm.frame(); + f.pendingTexHandles.add(1L); + f.pendingTexX.add(0); + f.pendingTexY.add(0); + f.pendingTexW.add(16); + f.pendingTexH.add(16); + f.pendingTexLevels.add(0); + f.pendingTexOffsets.add(0); + f.batchOffset = 1024; + assertEquals(1, f.pendingTexHandles.size(), "worker added one entry to its own list"); + }); final FrameManager.FrameState mine = fm.frame(); - assertEquals(1, workerListSize.get(), "worker added one entry to its own list"); assertTrue(mine.pendingTexHandles.isEmpty(), "test thread's batched-upload list is independent of worker's"); assertEquals(0, mine.batchOffset); } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/DepthStencilBitsTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/DepthStencilBitsTest.java index 3e5de26b0..a3aaacf5a 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/DepthStencilBitsTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/DepthStencilBitsTest.java @@ -41,21 +41,6 @@ class DepthStencilBitsTest { } } - @Test - void colorChannelBitsForCommonFormats() { - for (int fmt : new int[]{ SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM }) { - for (int c = 0; c < 4; c++) assertEquals(8, PixelOps.colorChannelBits(fmt, c), "channel " + c); - } - assertEquals(16, PixelOps.colorChannelBits(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, 3)); - assertEquals(8, PixelOps.colorChannelBits(SDL_GPU_TEXTUREFORMAT_R8_UNORM, 0)); - assertEquals(0, PixelOps.colorChannelBits(SDL_GPU_TEXTUREFORMAT_R8_UNORM, 1)); - assertEquals(8, PixelOps.colorChannelBits(SDL_GPU_TEXTUREFORMAT_R8G8_UNORM, 1)); - assertEquals(0, PixelOps.colorChannelBits(SDL_GPU_TEXTUREFORMAT_R8G8_UNORM, 2)); - assertEquals(10, PixelOps.colorChannelBits(SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM, 0)); - assertEquals(2, PixelOps.colorChannelBits(SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM, 3)); - assertEquals(0, PixelOps.colorChannelBits(SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT, 0)); - } - @Test void depthFormatsAreNotColorFormats() { assertTrue(PixelOps.isDepthFormat(SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT)); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/PixelFormatBitsTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/PixelFormatBitsTest.java index a9ca51983..3e0b671ff 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/PixelFormatBitsTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/PixelFormatBitsTest.java @@ -8,26 +8,20 @@ import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_A8_UNORM; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM; +import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM; +import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT; +import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_R8G8_UNORM; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_R8_UNORM; import static org.lwjgl.sdl.SDLGPU.SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; class PixelFormatBitsTest { - private static int[] rgba(int sdlFormat) { - return new int[]{ - colorChannelBits(sdlFormat, 0), - colorChannelBits(sdlFormat, 1), - colorChannelBits(sdlFormat, 2), - colorChannelBits(sdlFormat, 3), - }; - } - private static void assertRgba(int sdlFormat, int r, int g, int b, int a) { assertEquals(r, colorChannelBits(sdlFormat, 0), "red"); assertEquals(g, colorChannelBits(sdlFormat, 1), "green"); @@ -38,6 +32,9 @@ class PixelFormatBitsTest { @Test void uniformWidthFormats() { assertRgba(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, 8, 8, 8, 8); + assertRgba(SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, 8, 8, 8, 8); + assertRgba(SDL_GPU_TEXTUREFORMAT_R8G8_UNORM, 8, 8, 0, 0); + assertRgba(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, 16, 16, 16, 16); assertRgba(SDL_GPU_TEXTUREFORMAT_R8_UNORM, 8, 0, 0, 0); assertRgba(SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT, 16, 16, 0, 0); assertRgba(SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT, 32, 32, 32, 32); @@ -63,6 +60,5 @@ class PixelFormatBitsTest { void outOfRangeChannelsAreZero() { assertEquals(0, colorChannelBits(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, -1)); assertEquals(0, colorChannelBits(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, 4)); - assertEquals(4, rgba(SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM).length); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ReleasePersistentStagingTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ReleasePersistentStagingTest.java index 16213facc..2805014b5 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ReleasePersistentStagingTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ReleasePersistentStagingTest.java @@ -2,14 +2,14 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; -import com.gtnewhorizons.angelica.sdlgpu.device.Device; -import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; -import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -18,46 +18,30 @@ class ReleasePersistentStagingTest { @Test void nullPmAndNullStagingNoOp() { final ResourceManager rm = SdlTestRig.resourceManager(); - rm.releasePersistentStaging(null, 7, "unmap"); - rm.releasePersistentStaging(new PersistentMapping(null, 0L, 0L, 0), 7, "unmap"); + assertDoesNotThrow(() -> rm.releasePersistentStaging(null, 7, "unmap")); + assertDoesNotThrow(() -> rm.releasePersistentStaging(new PersistentMapping(null, 0L, 0L, 0), 7, "unmap")); } @Test - void noTransferThreadFreesInline() { + void noTransferThreadFreesInlineEvenWithAnInFlightSeq() { final ResourceManager rm = SdlTestRig.resourceManager(); - rm.releasePersistentStaging(new PersistentMapping(MemoryUtil.memAlloc(24), 0L, 24L, 0), 7, "unmap"); + final PersistentMapping pm = new PersistentMapping(MemoryUtil.memAlloc(24), 0L, 24L, 0); + pm.lastEnqueuedSeq = 50L; + assertDoesNotThrow(() -> rm.releasePersistentStaging(pm, 7, "unmap")); } - @Test - void seqAlreadySubmittedFreesInline() { + @ParameterizedTest(name = "lastEnqueuedSeq {0} leaves {1} pending") + @CsvSource({"0, 0", "50, 1"}) + void stagingFreeIsDeferredOnlyWhileItsSeqIsInFlight(long lastEnqueuedSeq, int expectedPending) { final SdlTestRig rig = SdlTestRig.create(); - final Device dev = rig.device; - final FrameManager fm = rig.frameManager; final ResourceManager rm = rig.resourceManager; - final TransferThread tt = new TransferThread(dev, rm); - rm.setTransferThread(tt); + final TransferThread tt = rig.installTransferThread(); try { final PersistentMapping pm = new PersistentMapping(MemoryUtil.memAlloc(24), 0L, 24L, 0); - pm.lastEnqueuedSeq = 0L; + pm.lastEnqueuedSeq = lastEnqueuedSeq; rm.releasePersistentStaging(pm, 7, "unmap"); - assertEquals(0, SdlReflect.pendingFreeCount(tt)); - } finally { shutdown(tt); } - } - - @Test - void inFlightSeqRoutesToFreeAfterSeq() { - final SdlTestRig rig = SdlTestRig.create(); - final Device dev = rig.device; - final FrameManager fm = rig.frameManager; - final ResourceManager rm = rig.resourceManager; - final TransferThread tt = new TransferThread(dev, rm); - rm.setTransferThread(tt); - try { - final PersistentMapping pm = new PersistentMapping(MemoryUtil.memAlloc(24), 0L, 24L, 0); - pm.lastEnqueuedSeq = 50L; - rm.releasePersistentStaging(pm, 7, "unmap"); - assertEquals(1, SdlReflect.pendingFreeCount(tt)); - } finally { shutdown(tt); } + assertEquals(expectedPending, SdlReflect.pendingFreeCount(tt)); + } finally { SdlTestRig.shutdown(tt); } } @Test @@ -74,11 +58,8 @@ class ReleasePersistentStagingTest { @Test void deleteBufferDefersFreeWhenSeqInFlight() { final SdlTestRig rig = SdlTestRig.create(); - final Device dev = rig.device; - final FrameManager fm = rig.frameManager; final ResourceManager rm = rig.resourceManager; - final TransferThread tt = new TransferThread(dev, rm); - rm.setTransferThread(tt); + final TransferThread tt = rig.installTransferThread(); try { final ByteBuffer staging = MemoryUtil.memCalloc(64); final PersistentMapping pm = new PersistentMapping(staging, 0L, staging.capacity(), 0); @@ -90,12 +71,6 @@ class ReleasePersistentStagingTest { assertNull(rm.getPersistentMapping(7)); assertEquals(1, SdlReflect.pendingFreeCount(tt)); - } finally { shutdown(tt); } - } - - private static void shutdown(TransferThread tt) { - tt.shutdown(); - try { tt.getThread().join(2000); } - catch (InterruptedException e) { Thread.currentThread().interrupt(); } + } finally { SdlTestRig.shutdown(tt); } } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ResourceManagerFormatMappingTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ResourceManagerFormatMappingTest.java index 67128be6d..7819cb59e 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ResourceManagerFormatMappingTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ResourceManagerFormatMappingTest.java @@ -1,11 +1,15 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL14; import org.lwjgl.opengl.GL21; import org.lwjgl.opengl.GL30; +import java.util.stream.Stream; + import static org.junit.jupiter.api.Assertions.*; import static org.lwjgl.sdl.SDLGPU.*; @@ -18,77 +22,39 @@ class ResourceManagerFormatMappingTest { return ResourceManager.mapTextureFormat(glFormat, D24, D24S8); } - @Test - void testRGBA8MapsCorrectly() { - assertEquals(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, map(GL11.GL_RGBA8)); - assertEquals(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, map(GL11.GL_RGBA)); + /** Unknown formats fall back to RGBA8; GL_DEPTH_COMPONENT32 must not take that fallback. */ + static Stream formats() { + return Stream.of( + Arguments.of("GL_RGBA8", GL11.GL_RGBA8, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM), + Arguments.of("GL_RGBA", GL11.GL_RGBA, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM), + Arguments.of("GL_RGB8", GL11.GL_RGB8, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM), + Arguments.of("GL_RGB", GL11.GL_RGB, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM), + Arguments.of("GL_SRGB8_ALPHA8", GL21.GL_SRGB8_ALPHA8, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB), + Arguments.of("GL_RGBA16F", GL30.GL_RGBA16F, SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT), + Arguments.of("GL_RGBA32F", GL30.GL_RGBA32F, SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT), + Arguments.of("GL_R16F", GL30.GL_R16F, SDL_GPU_TEXTUREFORMAT_R16_FLOAT), + Arguments.of("GL_RG16F", GL30.GL_RG16F, SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT), + Arguments.of("GL_R32F", GL30.GL_R32F, SDL_GPU_TEXTUREFORMAT_R32_FLOAT), + Arguments.of("GL_RG32F", GL30.GL_RG32F, SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT), + Arguments.of("GL_R8", GL30.GL_R8, SDL_GPU_TEXTUREFORMAT_R8_UNORM), + Arguments.of("GL_RG8", GL30.GL_RG8, SDL_GPU_TEXTUREFORMAT_R8G8_UNORM), + Arguments.of("GL_DEPTH_COMPONENT16", GL14.GL_DEPTH_COMPONENT16, SDL_GPU_TEXTUREFORMAT_D16_UNORM), + Arguments.of("GL_DEPTH_COMPONENT24", GL14.GL_DEPTH_COMPONENT24, SDL_GPU_TEXTUREFORMAT_D24_UNORM), + Arguments.of("GL_DEPTH_COMPONENT32F", GL30.GL_DEPTH_COMPONENT32F, SDL_GPU_TEXTUREFORMAT_D32_FLOAT), + Arguments.of("GL_RGBA16", GL11.GL_RGBA16, SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM), + Arguments.of("GL_DEPTH_COMPONENT32", GL14.GL_DEPTH_COMPONENT32, SDL_GPU_TEXTUREFORMAT_D32_FLOAT), + Arguments.of("GL_DEPTH24_STENCIL8", GL30.GL_DEPTH24_STENCIL8, SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT), + Arguments.of("GL_DEPTH32F_STENCIL8", GL30.GL_DEPTH32F_STENCIL8, SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT), + Arguments.of("GL_RGBA8UI", GL30.GL_RGBA8UI, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT), + Arguments.of("GL_RGBA32UI", GL30.GL_RGBA32UI, SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT), + Arguments.of("GL_R11F_G11F_B10F", GL30.GL_R11F_G11F_B10F, SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT), + Arguments.of("GL_RGB10_A2", GL11.GL_RGB10_A2, SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM), + Arguments.of("unknown format 0xDEAD", 0xDEAD, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM)); } - @Test - void testRGB8PromotesToRGBA8() { - assertEquals(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, map(GL11.GL_RGB8)); - assertEquals(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, map(GL11.GL_RGB)); - } - - @Test - void testSRGBAlpha() { - assertEquals(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB, map(GL21.GL_SRGB8_ALPHA8)); - } - - @Test - void testFloatFormats() { - assertEquals(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, map(GL30.GL_RGBA16F)); - assertEquals(SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT, map(GL30.GL_RGBA32F)); - assertEquals(SDL_GPU_TEXTUREFORMAT_R16_FLOAT, map(GL30.GL_R16F)); - assertEquals(SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT, map(GL30.GL_RG16F)); - assertEquals(SDL_GPU_TEXTUREFORMAT_R32_FLOAT, map(GL30.GL_R32F)); - assertEquals(SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT, map(GL30.GL_RG32F)); - } - - @Test - void testSingleChannelFormats() { - assertEquals(SDL_GPU_TEXTUREFORMAT_R8_UNORM, map(GL30.GL_R8)); - assertEquals(SDL_GPU_TEXTUREFORMAT_R8G8_UNORM, map(GL30.GL_RG8)); - } - - @Test - void testDepthFormats() { - assertEquals(SDL_GPU_TEXTUREFORMAT_D16_UNORM, map(GL14.GL_DEPTH_COMPONENT16)); - assertEquals(SDL_GPU_TEXTUREFORMAT_D24_UNORM, map(GL14.GL_DEPTH_COMPONENT24)); - assertEquals(SDL_GPU_TEXTUREFORMAT_D32_FLOAT, map(GL30.GL_DEPTH_COMPONENT32F)); - } - - @Test - void testRGBA16MapsToR16G16B16A16Unorm() { - assertEquals(SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM, map(GL11.GL_RGBA16)); - } - - @Test - void testDepthComponent32MapsToD32Float() { - assertEquals(SDL_GPU_TEXTUREFORMAT_D32_FLOAT, map(GL14.GL_DEPTH_COMPONENT32)); - assertNotEquals(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, map(GL14.GL_DEPTH_COMPONENT32), "must not fall through to the colour-format default on a depth attachment"); - } - - @Test - void testDepthStencilFormats() { - assertEquals(SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT, map(GL30.GL_DEPTH24_STENCIL8)); - assertEquals(SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT, map(GL30.GL_DEPTH32F_STENCIL8)); - } - - @Test - void testIntegerFormats() { - assertEquals(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT, map(GL30.GL_RGBA8UI)); - assertEquals(SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT, map(GL30.GL_RGBA32UI)); - } - - @Test - void testPackedFormats() { - assertEquals(SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT, map(GL30.GL_R11F_G11F_B10F)); - assertEquals(SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM, map(GL11.GL_RGB10_A2)); - } - - @Test - void testUnknownFormatFallsBackToRGBA8() { - assertEquals(SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, map(0xDEAD)); + @ParameterizedTest(name = "{0}") + @MethodSource("formats") + void mapsGlInternalFormat(String name, int glFormat, int expectedSdlFormat) { + assertEquals(expectedSdlFormat, map(glFormat)); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ResourceManagerUsageCombinationTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ResourceManagerUsageCombinationTest.java index c0b74c80e..aeeac39ef 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ResourceManagerUsageCombinationTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/ResourceManagerUsageCombinationTest.java @@ -1,6 +1,11 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; import static org.lwjgl.sdl.SDLGPU.*; @@ -24,27 +29,20 @@ class ResourceManagerUsageCombinationTest { assertNotEquals(0, out & SDL_GPU_TEXTUREUSAGE_SAMPLER); } - @Test - void samplerKeepsPlainComputeStorage() { - final int merged = SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE; - assertEquals(merged, ResourceManager.reconcileUsage(merged), "SDL transitions plain compute storage symmetrically to and from the sampler default"); + /** Plain compute storage survives because SDL transitions it symmetrically to and from the sampler default. */ + static Stream unchangedUsages() { + return Stream.of( + Arguments.of("sampler + plain compute storage", + SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE), + Arguments.of("every storage bit without sampler", + SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE), + Arguments.of("plain sampled colour target", SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET), + Arguments.of("sampled depth target", SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)); } - @Test - void withoutSamplerEveryStorageBitSurvives() { - final int merged = SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE; - assertEquals(merged, ResourceManager.reconcileUsage(merged)); - } - - @Test - void plainSampledColourTextureIsUnchanged() { - final int merged = SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; - assertEquals(merged, ResourceManager.reconcileUsage(merged)); - } - - @Test - void depthTargetIsUnchanged() { - final int merged = SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; + @ParameterizedTest(name = "{0}") + @MethodSource("unchangedUsages") + void usageIsUnchanged(String name, int merged) { assertEquals(merged, ResourceManager.reconcileUsage(merged)); } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TextureReleaseUploadOrderTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TextureReleaseUploadOrderTest.java index 1e05053ae..73eb352fd 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TextureReleaseUploadOrderTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TextureReleaseUploadOrderTest.java @@ -33,8 +33,7 @@ class TextureReleaseUploadOrderTest { void releaseIsDeferredWhileTextureUploadsAreStillQueued() { final SdlTestRig rig = SdlTestRig.create(); final ResourceManager rm = rig.resourceManager; - final TransferThread tt = new TransferThread(rig.device, rm); - rm.setTransferThread(tt); + final TransferThread tt = rig.installTransferThread(); setUploadSeqs(rm, 50L); Reflect.setDeclared(TransferThread.class, tt, "submittedSeq", 10L); @@ -50,8 +49,7 @@ class TextureReleaseUploadOrderTest { void releaseIsImmediateOnceTheTransferThreadHasCaughtUp() { final SdlTestRig rig = SdlTestRig.create(); final ResourceManager rm = rig.resourceManager; - final TransferThread tt = new TransferThread(rig.device, rm); - rm.setTransferThread(tt); + final TransferThread tt = rig.installTransferThread(); setUploadSeqs(rm, 50L); Reflect.setDeclared(TransferThread.class, tt, "submittedSeq", 49L); @@ -97,8 +95,7 @@ class TextureReleaseUploadOrderTest { void reallocReleaseObeysTheSameRule() { final SdlTestRig rig = SdlTestRig.create(); final ResourceManager rm = rig.resourceManager; - final TransferThread tt = new TransferThread(rig.device, rm); - rm.setTransferThread(tt); + final TransferThread tt = rig.installTransferThread(); setUploadSeqs(rm, 7L); Reflect.setDeclared(TransferThread.class, tt, "submittedSeq", 0L); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TextureSamplerStateTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TextureSamplerStateTest.java index 5d1a77258..15a914f09 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TextureSamplerStateTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TextureSamplerStateTest.java @@ -1,46 +1,42 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL14; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class TextureSamplerStateTest { - @Test - void changedIntValueReportsChange() { - final TextureSamplerState ss = new TextureSamplerState(); - assertTrue(ss.seti(GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR)); - assertEquals(GL11.GL_LINEAR, ss.minFilter); - assertFalse(ss.seti(GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR)); + private static Arguments setter(String name, Predicate set, Function field, Number expected) { + return Arguments.of(name, set, field, expected); } - @Test - void changedFloatValueReportsChange() { - final TextureSamplerState ss = new TextureSamplerState(); - assertTrue(ss.setf(GL14.GL_TEXTURE_LOD_BIAS, 0.5f)); - assertEquals(0.5f, ss.lodBias); - assertFalse(ss.setf(GL14.GL_TEXTURE_LOD_BIAS, 0.5f)); + static Stream setters() { + return Stream.of( + setter("seti on an int pname", ss -> ss.seti(GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR), ss -> ss.minFilter, GL11.GL_LINEAR), + setter("setf on a float pname", ss -> ss.setf(GL14.GL_TEXTURE_LOD_BIAS, 0.5f), ss -> ss.lodBias, 0.5f), + setter("setf falls through to an int pname", ss -> ss.setf(GL11.GL_TEXTURE_MIN_FILTER, (float) GL11.GL_LINEAR), ss -> ss.minFilter, GL11.GL_LINEAR), + setter("seti routes LOD to the float field", ss -> ss.seti(GL12.GL_TEXTURE_MIN_LOD, 2), ss -> ss.minLod, 2.0f)); } - @Test - void setfFallsThroughToIntPname() { + @ParameterizedTest(name = "{0}") + @MethodSource("setters") + void setReportsChangeOnlyWhenTheValueChanges(String name, Predicate set, Function field, Number expected) { final TextureSamplerState ss = new TextureSamplerState(); - assertTrue(ss.setf(GL11.GL_TEXTURE_MIN_FILTER, (float) GL11.GL_LINEAR)); - assertEquals(GL11.GL_LINEAR, ss.minFilter); - assertFalse(ss.setf(GL11.GL_TEXTURE_MIN_FILTER, (float) GL11.GL_LINEAR)); - } - - @Test - void setiRoutesLodToTheFloatField() { - final TextureSamplerState ss = new TextureSamplerState(); - assertTrue(ss.seti(GL12.GL_TEXTURE_MIN_LOD, 2)); - assertEquals(2.0f, ss.minLod); - assertFalse(ss.seti(GL12.GL_TEXTURE_MIN_LOD, 2)); + assertTrue(set.test(ss), "first set changes the value"); + assertEquals(expected, field.apply(ss)); + assertFalse(set.test(ss), "setting the same value again is not a change"); } @Test diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TransferRetireSeqTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TransferRetireSeqTest.java index 9f4f1a3c3..0a65d35a4 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TransferRetireSeqTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TransferRetireSeqTest.java @@ -13,14 +13,8 @@ class TransferRetireSeqTest { private static TransferThread idleThread() { final SdlTestRig rig = SdlTestRig.create(); - final TransferThread tt = new TransferThread(rig.device, rig.resourceManager); - tt.shutdown(); - try { - tt.getThread().join(5_000L); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new AssertionError("interrupted while stopping the transfer thread", e); - } + final TransferThread tt = rig.newTransferThread(); + SdlTestRig.shutdown(tt); assertFalse(tt.getThread().isAlive(), "the worker must be stopped first: its loop zeroes openHighestSeq after publishing, so a live worker races this test"); return tt; } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TransferThreadDeferFreeTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TransferThreadDeferFreeTest.java index 6a77afdd7..692d82a7b 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TransferThreadDeferFreeTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TransferThreadDeferFreeTest.java @@ -2,6 +2,8 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; @@ -11,26 +13,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals; class TransferThreadDeferFreeTest { private static TransferThread newThread() { - final SdlTestRig rig = SdlTestRig.create(); - return new TransferThread(rig.device, rig.resourceManager); + return SdlTestRig.create().newTransferThread(); } - @Test - void seqAlreadySubmittedFreesInline() { + @ParameterizedTest(name = "seq {0} leaves {1} pending") + @CsvSource({"0, 0", "100, 1"}) + void freeIsDeferredOnlyForAnUnsubmittedSeq(long seq, int expectedPending) { final TransferThread tt = newThread(); try { - tt.freeAfterSeq(MemoryUtil.memAlloc(24), 0L); - assertEquals(0, SdlReflect.pendingFreeCount(tt)); - } finally { shutdown(tt); } - } - - @Test - void futureSeqDefers() { - final TransferThread tt = newThread(); - try { - tt.freeAfterSeq(MemoryUtil.memAlloc(24), 100L); - assertEquals(1, SdlReflect.pendingFreeCount(tt)); - } finally { shutdown(tt); } + tt.freeAfterSeq(MemoryUtil.memAlloc(24), seq); + assertEquals(expectedPending, SdlReflect.pendingFreeCount(tt)); + } finally { SdlTestRig.shutdown(tt); } } @Test @@ -40,7 +33,7 @@ class TransferThreadDeferFreeTest { tt.freeAfterSeq(buf, Long.MAX_VALUE); assertEquals(1, SdlReflect.pendingFreeCount(tt)); - shutdown(tt); + SdlTestRig.shutdown(tt); assertEquals(0, SdlReflect.pendingFreeCount(tt)); } @@ -51,12 +44,6 @@ class TransferThreadDeferFreeTest { tt.freeAfterSeq(null, 0L); tt.freeAfterSeq(null, 999L); assertEquals(0, SdlReflect.pendingFreeCount(tt)); - } finally { shutdown(tt); } - } - - private static void shutdown(TransferThread tt) { - tt.shutdown(); - try { tt.getThread().join(2000); } - catch (InterruptedException e) { Thread.currentThread().interrupt(); } + } finally { SdlTestRig.shutdown(tt); } } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TransferThreadWakePolicyTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TransferThreadWakePolicyTest.java index 5549f2961..d3a5e7d7f 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TransferThreadWakePolicyTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/TransferThreadWakePolicyTest.java @@ -3,6 +3,8 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import java.util.concurrent.locks.LockSupport; @@ -13,10 +15,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class TransferThreadWakePolicyTest { - @Test - void wakesOnceEveryCoalesceWindow() { + @ParameterizedTest(name = "from seq {0}") + @ValueSource(longs = {1L, 1L << 40}) + void wakesOnceEveryCoalesceWindow(long base) { int woken = 0; - for (long seq = 1; seq <= 1600; seq++) { + for (long seq = base; seq < base + 1600; seq++) { if (TransferThread.shouldWakeOnEnqueue(seq)) woken++; } assertEquals(100, woken, "1600 enqueues must produce 100 unparks, not 1600"); @@ -39,26 +42,10 @@ class TransferThreadWakePolicyTest { assertFalse(TransferThread.shouldWakeOnEnqueue(17)); } - @Test - void policyHoldsAtLargeSequenceNumbers() { - final long base = 1L << 40; - int woken = 0; - for (long seq = base; seq < base + 1600; seq++) { - if (TransferThread.shouldWakeOnEnqueue(seq)) woken++; - } - assertEquals(100, woken); - } - private static TransferThread stoppedThread(long submittedSeq) { final SdlTestRig rig = SdlTestRig.create(); - final TransferThread tt = new TransferThread(rig.device, rig.resourceManager); - tt.shutdown(); - try { - tt.getThread().join(5_000L); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new AssertionError(e); - } + final TransferThread tt = rig.newTransferThread(); + SdlTestRig.shutdown(tt); assertFalse(tt.getThread().isAlive()); Reflect.set(tt, "submittedSeq", submittedSeq); return tt; diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/UploadArenaGpuRoundTripTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/UploadArenaGpuRoundTripTest.java index c273aa830..6b63e3cd3 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/UploadArenaGpuRoundTripTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/UploadArenaGpuRoundTripTest.java @@ -1,15 +1,16 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; +import com.gtnewhorizons.angelica.sdlgpu.RealDeviceExtension; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; import com.gtnewhorizons.angelica.sdlgpu.device.Device; import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; import org.lwjgl.PointerBuffer; import org.lwjgl.sdl.SDLError; import org.lwjgl.sdl.SDL_GPUBufferCreateInfo; @@ -25,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.lwjgl.sdl.SDLGPU.*; import static org.lwjgl.system.MemoryStack.stackPush; +@ExtendWith(RealDeviceExtension.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class UploadArenaGpuRoundTripTest { @@ -36,21 +38,13 @@ class UploadArenaGpuRoundTripTest { @BeforeAll static void setUp() throws Exception { - final SdlTestRig rig = SdlTestRig.acquireRealDevice(); + final SdlTestRig rig = RealDeviceExtension.rig(); device = rig.device; frameManager = rig.frameManager; resourceManager = rig.resourceManager; sdlDevice = rig.sdlHandle; } - @AfterAll - static void tearDown() { - if (frameManager != null && frameManager.isFrameActive()) { - frameManager.endFrame(); - } - SdlTestRig.releaseRealDevice(); - } - private static void beginFrame() { frameManager.beginFrame(); frameManager.frame().swapchainUnavailable = true; // headless: never touch the swapchain diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/UploadCBAutoFlushThresholdTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/UploadCBAutoFlushThresholdTest.java index b774af46a..2a03410cd 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/UploadCBAutoFlushThresholdTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/UploadCBAutoFlushThresholdTest.java @@ -1,15 +1,15 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; +import com.gtnewhorizons.angelica.glsm.testutil.TestThreads; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; - -import com.gtnewhorizons.angelica.sdlgpu.device.Device; import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; import static com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager.MAX_PENDING_UPLOAD_BYTES; import static com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager.MAX_PENDING_UPLOAD_COMMANDS; @@ -17,34 +17,24 @@ import static org.junit.jupiter.api.Assertions.*; class UploadCBAutoFlushThresholdTest { - private static FrameManager newFM() { - return SdlTestRig.frameManager(); + static Stream byteCounts() { + return Stream.of( + Arguments.of("half the threshold", MAX_PENDING_UPLOAD_BYTES / 2, false), + Arguments.of("at the threshold", MAX_PENDING_UPLOAD_BYTES, true), + Arguments.of("one byte over the threshold", MAX_PENDING_UPLOAD_BYTES + 1, true)); } - @Test - void bytesUnderThreshold_doesNotRequestFlush() { - final FrameManager fm = newFM(); - fm.recordUploadCommands(MAX_PENDING_UPLOAD_BYTES / 2, 1); - assertFalse(fm.shouldAutoSubmitPendingUpload()); - } - - @Test - void bytesAtThreshold_requestsFlush() { - final FrameManager fm = newFM(); - fm.recordUploadCommands(MAX_PENDING_UPLOAD_BYTES, 1); - assertTrue(fm.shouldAutoSubmitPendingUpload()); - } - - @Test - void bytesOverThreshold_requestsFlush() { - final FrameManager fm = newFM(); - fm.recordUploadCommands(MAX_PENDING_UPLOAD_BYTES + 1, 1); - assertTrue(fm.shouldAutoSubmitPendingUpload()); + @ParameterizedTest(name = "{0}") + @MethodSource("byteCounts") + void flushIsRequestedFromTheByteThreshold(String name, long bytes, boolean expectFlush) { + final FrameManager fm = SdlTestRig.frameManager(); + fm.recordUploadCommands(bytes, 1); + assertEquals(expectFlush, fm.shouldAutoSubmitPendingUpload()); } @Test void manyTinyUploadsAccumulateBytes() { - final FrameManager fm = newFM(); + final FrameManager fm = SdlTestRig.frameManager(); for (int i = 0; i < 255; i++) fm.recordUploadCommands(65536, 1); assertTrue(fm.frame().pendingUploadBytes < MAX_PENDING_UPLOAD_BYTES); fm.recordUploadCommands(65536, 1); @@ -53,7 +43,7 @@ class UploadCBAutoFlushThresholdTest { @Test void manyOneByteUploadsTripCommandThreshold() { - final FrameManager fm = newFM(); + final FrameManager fm = SdlTestRig.frameManager(); for (int i = 0; i < MAX_PENDING_UPLOAD_COMMANDS - 1; i++) fm.recordUploadCommands(1, 1); assertFalse(fm.shouldAutoSubmitPendingUpload(), "below command threshold and far below byte threshold"); fm.recordUploadCommands(1, 1); @@ -62,24 +52,14 @@ class UploadCBAutoFlushThresholdTest { @Test void recordUploadCommands_perThreadCountersDoNotInterfere() throws Exception { - final FrameManager fm = newFM(); - final AtomicLong threadABytes = new AtomicLong(); - final AtomicReference threadAShouldFlush = new AtomicReference<>(); - final CountDownLatch aDone = new CountDownLatch(1); - final Thread tA = new Thread(() -> { - try { - fm.recordUploadCommands(MAX_PENDING_UPLOAD_BYTES + 1, 17); - threadABytes.set(fm.frame().pendingUploadBytes); - threadAShouldFlush.set(fm.shouldAutoSubmitPendingUpload()); - } finally { - aDone.countDown(); - } - }, "upload-thread-A"); - tA.start(); - aDone.await(); + final FrameManager fm = SdlTestRig.frameManager(); + + TestThreads.run("upload-thread-A", () -> { + fm.recordUploadCommands(MAX_PENDING_UPLOAD_BYTES + 1, 17); + assertEquals(MAX_PENDING_UPLOAD_BYTES + 1, fm.frame().pendingUploadBytes); + assertTrue(fm.shouldAutoSubmitPendingUpload()); + }); - assertEquals(MAX_PENDING_UPLOAD_BYTES + 1, threadABytes.get()); - assertTrue(threadAShouldFlush.get()); assertEquals(0L, fm.frame().pendingUploadBytes); assertEquals(0, fm.frame().pendingUploadCommands); assertFalse(fm.shouldAutoSubmitPendingUpload()); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/UploadCBBatchFillCycleSafetyTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/UploadCBBatchFillCycleSafetyTest.java index 2b54e9c56..eed93fa59 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/UploadCBBatchFillCycleSafetyTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/resource/UploadCBBatchFillCycleSafetyTest.java @@ -1,27 +1,22 @@ package com.gtnewhorizons.angelica.sdlgpu.resource; +import com.gtnewhorizons.angelica.glsm.testutil.TestThreads; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; import com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import static com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager.MAX_PENDING_UPLOAD_BYTES; -import static com.gtnewhorizons.angelica.sdlgpu.frame.FrameManager.MAX_PENDING_UPLOAD_COMMANDS; import static org.junit.jupiter.api.Assertions.*; class UploadCBBatchFillCycleSafetyTest { - private static FrameManager newFM() { - return SdlTestRig.frameManager(); - } - @Test void thresholdPredicate_dependsOnlyOnCounters() { - final FrameManager fm = newFM(); + final FrameManager fm = SdlTestRig.frameManager(); final FrameManager.FrameState f = fm.frame(); fm.recordUploadCommands(MAX_PENDING_UPLOAD_BYTES + 1, 1); f.copyPass = 0; @@ -30,47 +25,9 @@ class UploadCBBatchFillCycleSafetyTest { assertTrue(fm.shouldAutoSubmitPendingUpload()); } - @Test - void thresholdPredicate_commandsThresholdTrips() { - final FrameManager fm = newFM(); - for (int i = 0; i < MAX_PENDING_UPLOAD_COMMANDS; i++) fm.recordUploadCommands(1, 1); - assertTrue(fm.shouldAutoSubmitPendingUpload(), "command-count threshold trips at MAX_PENDING_UPLOAD_COMMANDS"); - } - - @Test - void flushRequestedFlag_isPerThread() throws Exception { - final FrameManager fm = newFM(); - final FrameManager.FrameState mine = fm.frame(); - assertFalse(mine.flushRequested, "test thread's flag starts false"); - - final AtomicReference workerInitial = new AtomicReference<>(); - final AtomicReference workerAfterSet = new AtomicReference<>(); - final AtomicBoolean workerWasIndependentObject = new AtomicBoolean(); - final CountDownLatch done = new CountDownLatch(1); - - final Thread worker = new Thread(() -> { - try { - final FrameManager.FrameState theirs = fm.frame(); - workerInitial.set(theirs.flushRequested); - workerWasIndependentObject.set(theirs != mine); - theirs.flushRequested = true; - workerAfterSet.set(theirs.flushRequested); - } finally { - done.countDown(); - } - }, "flush-flag-worker"); - worker.start(); - done.await(); - - assertEquals(Boolean.FALSE, workerInitial.get()); - assertTrue(workerWasIndependentObject.get(), "worker has its own FrameManager.FrameState"); - assertEquals(Boolean.TRUE, workerAfterSet.get()); - assertFalse(mine.flushRequested, "test thread's flag stays false"); - } - @Test void requestFlushOnAllRegisteredFrames_flagsAllIncludingCaller() throws Exception { - final FrameManager fm = newFM(); + final FrameManager fm = SdlTestRig.frameManager(); final FrameManager.FrameState mine = fm.frame(); final AtomicReference wA = new AtomicReference<>(); @@ -78,18 +35,16 @@ class UploadCBBatchFillCycleSafetyTest { final CountDownLatch registered = new CountDownLatch(2); final CountDownLatch keepAlive = new CountDownLatch(1); - final Thread tA = new Thread(() -> { + final TestThreads.Worker tA = TestThreads.start("worker-A", () -> { wA.set(fm.frame()); registered.countDown(); - try { keepAlive.await(); } catch (InterruptedException ignored) {} - }, "worker-A"); - final Thread tB = new Thread(() -> { + keepAlive.await(); + }); + final TestThreads.Worker tB = TestThreads.start("worker-B", () -> { wB.set(fm.frame()); registered.countDown(); - try { keepAlive.await(); } catch (InterruptedException ignored) {} - }, "worker-B"); - tA.start(); - tB.start(); + keepAlive.await(); + }); registered.await(); assertFalse(mine.flushRequested); @@ -109,23 +64,14 @@ class UploadCBBatchFillCycleSafetyTest { @Test void releaseThreadState_unregistersFromRegistry() throws Exception { - final FrameManager fm = newFM(); + final FrameManager fm = SdlTestRig.frameManager(); fm.frame(); final AtomicReference beforeRelease = new AtomicReference<>(); - final CountDownLatch released = new CountDownLatch(1); - - final Thread w = new Thread(() -> { - try { - beforeRelease.set(fm.frame()); - fm.releaseThreadState(); - } finally { - released.countDown(); - } - }, "release-worker"); - w.start(); - released.await(); - w.join(); + TestThreads.run("release-worker", () -> { + beforeRelease.set(fm.frame()); + fm.releaseThreadState(); + }); final FrameManager.FrameState dead = beforeRelease.get(); assertNotNull(dead); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/sampler/SamplerBindGenerationTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/sampler/SamplerBindGenerationTest.java index a510e2053..f16656ee3 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/sampler/SamplerBindGenerationTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/sampler/SamplerBindGenerationTest.java @@ -1,6 +1,5 @@ package com.gtnewhorizons.angelica.sdlgpu.sampler; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.sdlgpu.SDLGPURenderBackend; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; @@ -9,13 +8,11 @@ import org.junit.jupiter.api.Test; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; class SamplerBindGenerationTest { - @Test void switchingActiveUnitDoesNotBumpTheGeneration() { final SDLGPURenderBackend backend = new SDLGPURenderBackend(); @@ -74,7 +71,7 @@ class SamplerBindGenerationTest { void settingAnUnchangedTexParameterOnAResolvedSamplerDoesNotBumpTheGeneration() { final SDLGPURenderBackend backend = new SDLGPURenderBackend(); final ContextState st = SdlTestRig.contextState(); - final ResourceManager resourceManager = Reflect.get(backend, "resourceManager"); + final ResourceManager resourceManager = SdlTestRig.resourceManagerOf(backend); backend.activeTexture(GL13.GL_TEXTURE0); backend.bindTexture(GL11.GL_TEXTURE_2D, 42); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/sampler/SamplerCacheTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/sampler/SamplerCacheTest.java index cabec0844..7727f552d 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/sampler/SamplerCacheTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/sampler/SamplerCacheTest.java @@ -7,7 +7,6 @@ import java.util.concurrent.atomic.AtomicLong; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; class SamplerCacheTest { @@ -67,16 +66,4 @@ class SamplerCacheTest { assertEquals(2, calls.get(), "0 result must not be cached -- factory must be invoked again"); assertEquals(0, cache.size()); } - - @Test - void recordKeyEqualityAndHashCode() { - final SamplerCache.Key a = key(0x2702, 0x2600); - final SamplerCache.Key b = key(0x2702, 0x2600); - final SamplerCache.Key c = key(0x2600, 0x2600); - - assertEquals(a, b); - assertEquals(a.hashCode(), b.hashCode()); - assertNotEquals(a, c); - assertNotNull(a.toString()); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/CanonicalBlockLayoutTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/CanonicalBlockLayoutTest.java index 428ff6e72..0cf599ea4 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/CanonicalBlockLayoutTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/CanonicalBlockLayoutTest.java @@ -9,10 +9,10 @@ import java.nio.ByteBuffer; import java.util.LinkedHashMap; import java.util.Map; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.fail; class CanonicalBlockLayoutTest { @@ -69,7 +69,7 @@ class CanonicalBlockLayoutTest { } private static Map offsetsOf(String source) { - final ByteBuffer spirv = compile(source); + final ByteBuffer spirv = compileOrFail(source, Shaderc.shaderc_fragment_shader, SpirvCompiler.Options.vulkanRelaxed()); try { final ShaderManager.StageReflection refl = ShaderManager.reflectStage(spirv, false); final Map out = new LinkedHashMap<>(); @@ -79,12 +79,4 @@ class CanonicalBlockLayoutTest { MemoryUtil.memFree(spirv); } } - - private static ByteBuffer compile(String source) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, Shaderc.shaderc_fragment_shader, "test.frag", SpirvCompiler.Options.vulkanRelaxed()); - if (r.spirv() == null) { - fail("shaderc failed: " + r.error()); - } - return r.spirv(); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ClearImage3DReflectionTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ClearImage3DReflectionTest.java index 50748e3f8..12170d96e 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ClearImage3DReflectionTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ClearImage3DReflectionTest.java @@ -1,7 +1,6 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import com.gtnewhorizons.angelica.glsm.testutil.TestPaths; import org.junit.jupiter.api.Test; import org.lwjgl.opengl.GL43; @@ -11,8 +10,8 @@ import org.lwjgl.util.shaderc.Shaderc; import java.nio.ByteBuffer; import java.util.List; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; class ClearImage3DReflectionTest { @@ -61,9 +60,7 @@ class ClearImage3DReflectionTest { void looseUniformsReflectAsBindingZeroUboMembers() { final GlslVulkanPreprocess.Result pre = GlslVulkanPreprocess.run(source(), GL43.GL_COMPUTE_SHADER, SHADER, true); final String src = pre != null ? pre.rewrittenSource() : source(); - final SpirvCompiler.Result r = SpirvCompiler.compile(src, Shaderc.shaderc_compute_shader, SHADER, SpirvCompiler.Options.vulkanForced460Core()); - if (r.spirv() == null) fail(SHADER + " failed to compile: " + r.error() + "\nsource:\n" + src); - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(src, Shaderc.shaderc_compute_shader); try { ShaderManager.remapSpirvForComputeSDLGPU(spirv); final ShaderManager.StageReflection refl = ShaderManager.reflectStage(spirv, false); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ComputeSpirvRemapTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ComputeSpirvRemapTest.java index 3292c750f..2b30afcc1 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ComputeSpirvRemapTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ComputeSpirvRemapTest.java @@ -5,7 +5,6 @@ import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.TreeSet; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import org.junit.jupiter.api.Test; import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; @@ -18,6 +17,7 @@ import org.lwjgl.util.spvc.SpvcReflectedResource; import java.nio.ByteBuffer; import java.nio.IntBuffer; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -52,7 +52,7 @@ class ComputeSpirvRemapTest { @Test void mixedRoRwLayout_setsAndBindings() { - final ByteBuffer spirv = compile(COMPUTE_MIXED, Shaderc.shaderc_compute_shader); + final ByteBuffer spirv = compileOrFail(COMPUTE_MIXED, Shaderc.shaderc_compute_shader); try { final ShaderManager.ComputeBindingMap map = ShaderManager.remapSpirvForComputeSDLGPU(spirv); @@ -207,7 +207,7 @@ class ComputeSpirvRemapTest { @Test void looseUniforms_flaggedAsDefaultBlock() { - final ByteBuffer spirv = compile(COMPUTE_LOOSE_UNIFORMS, Shaderc.shaderc_compute_shader); + final ByteBuffer spirv = compileOrFail(COMPUTE_LOOSE_UNIFORMS, Shaderc.shaderc_compute_shader); try { final ShaderManager.ComputeBindingMap map = ShaderManager.remapSpirvForComputeSDLGPU(spirv); assertEquals(1, map.uboGlSlots().length, "one synthetic UBO"); @@ -226,7 +226,7 @@ class ComputeSpirvRemapTest { @Test void noResources_emptyBindingMap() { - final ByteBuffer spirv = compile(COMPUTE_NO_RESOURCES, Shaderc.shaderc_compute_shader); + final ByteBuffer spirv = compileOrFail(COMPUTE_NO_RESOURCES, Shaderc.shaderc_compute_shader); try { final ShaderManager.ComputeBindingMap map = ShaderManager.remapSpirvForComputeSDLGPU(spirv); assertEquals(0, map.roSsboGlSlots().length); @@ -242,7 +242,7 @@ class ComputeSpirvRemapTest { @Test void mixedRoRw_reflectionSplitCounts() { - final ByteBuffer spirv = compile(COMPUTE_MIXED, Shaderc.shaderc_compute_shader); + final ByteBuffer spirv = compileOrFail(COMPUTE_MIXED, Shaderc.shaderc_compute_shader); try { final ShaderManager.StageReflection refl = ShaderManager.reflectStage(spirv, false); assertEquals(2, refl.numReadonlyStorageBuffers()); @@ -258,12 +258,4 @@ class ComputeSpirvRemapTest { for (int v : arr) if (v == value) return; fail("expected " + value + " in " + Arrays.toString(arr)); } - - private static ByteBuffer compile(String source, int shaderKind) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, shaderKind, "compute_test", SpirvCompiler.Options.vulkanForced460Core()); - if (r.spirv() == null) { - fail("compute compile failed: " + r.error()); - } - return r.spirv(); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ComputeStorageImageReflectionTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ComputeStorageImageReflectionTest.java index 5f622d94a..cfb0ab096 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ComputeStorageImageReflectionTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ComputeStorageImageReflectionTest.java @@ -1,6 +1,5 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import com.gtnewhorizons.angelica.glsm.testutil.TestPaths; import com.gtnewhorizons.angelica.glsm.texture.InternalTextureFormat; import org.junit.jupiter.api.Test; @@ -11,15 +10,13 @@ import org.lwjgl.util.shaderc.Shaderc; import java.nio.ByteBuffer; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; class ComputeStorageImageReflectionTest { private static ShaderManager.ComputeBindingMap reflect(String name, String source) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, Shaderc.shaderc_compute_shader, name, SpirvCompiler.Options.vulkanForced460Core()); - if (r.spirv() == null) fail(name + " failed to compile: " + r.error()); - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(source, Shaderc.shaderc_compute_shader); try { return ShaderManager.remapSpirvForComputeSDLGPU(spirv); } finally { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/FontFilterVertexSamplerTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/FontFilterVertexSamplerTest.java index f3ce6aa37..adb3a559c 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/FontFilterVertexSamplerTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/FontFilterVertexSamplerTest.java @@ -1,7 +1,6 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import com.gtnewhorizons.angelica.glsm.testutil.TestPaths; import com.gtnewhorizons.angelica.sdlgpu.sampler.SamplerLookup; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; @@ -12,8 +11,8 @@ import org.lwjgl.util.shaderc.Shaderc; import java.nio.ByteBuffer; import java.util.List; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.lwjgl.system.MemoryUtil.memFree; @@ -30,10 +29,7 @@ class FontFilterVertexSamplerTest { } private static ShaderManager.StageReflection reflect(String source) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, Shaderc.shaderc_vertex_shader, - "fontFilter.vsh", SpirvCompiler.Options.vulkanForced460Core()); - assertNotNull(r.spirv(), () -> "fontFilter.vsh failed to compile for Vulkan: " + r.error()); - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(source, Shaderc.shaderc_vertex_shader); try { return ShaderManager.reflectStage(spirv, true); } finally { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/FragmentOutputReflectionTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/FragmentOutputReflectionTest.java index a7e56f257..b95530200 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/FragmentOutputReflectionTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/FragmentOutputReflectionTest.java @@ -1,22 +1,22 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; - import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.util.shaderc.Shaderc; import java.nio.ByteBuffer; +import java.util.stream.Stream; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.lwjgl.system.MemoryUtil.memFree; class FragmentOutputReflectionTest { private static int maxOutputLocationOf(String source) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, Shaderc.shaderc_fragment_shader, "out.frag", SpirvCompiler.Options.vulkanForced460Core()); - assertNotNull(r.spirv(), () -> "compile failed: " + r.error()); - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(source, Shaderc.shaderc_fragment_shader); try { return ShaderManager.reflectStage(spirv, false).maxOutputLocation(); } finally { @@ -24,59 +24,51 @@ class FragmentOutputReflectionTest { } } - @Test - void irisStyleMultiTargetOutputs() { - assertEquals(3, maxOutputLocationOf(""" - #version 460 core - layout ( location = 3 ) out vec4 iris_FragData3 ; - layout ( location = 2 ) out vec4 iris_FragData2 ; - layout ( location = 1 ) out vec4 iris_FragData1 ; - layout ( location = 0 ) out vec4 iris_FragData0 ; - void main() { - iris_FragData0 = vec4(1.0); - iris_FragData1 = vec4(2.0); - iris_FragData2 = vec4(3.0); - iris_FragData3 = vec4(4.0); - } - """)); + static Stream fragments() { + return Stream.of( + Arguments.of("irisStyleMultiTargetOutputs", 3, """ + #version 460 core + layout ( location = 3 ) out vec4 iris_FragData3 ; + layout ( location = 2 ) out vec4 iris_FragData2 ; + layout ( location = 1 ) out vec4 iris_FragData1 ; + layout ( location = 0 ) out vec4 iris_FragData0 ; + void main() { + iris_FragData0 = vec4(1.0); + iris_FragData1 = vec4(2.0); + iris_FragData2 = vec4(3.0); + iris_FragData3 = vec4(4.0); + } + """), + Arguments.of("twoTargetOutputs", 1, """ + #version 460 core + layout ( location = 1 ) out vec4 a ; + layout ( location = 0 ) out vec4 b ; + void main() { a = vec4(1.0); b = vec4(0.0); } + """), + Arguments.of("implicitLocationZeroIsFound", 0, """ + #version 460 core + out vec4 fragColor; + void main() { fragColor = vec4(1.0); } + """), + Arguments.of("noOutputsYieldsMinusOne", -1, """ + #version 460 core + void main() { } + """)); } - @Test - void twoTargetOutputs() { - assertEquals(1, maxOutputLocationOf(""" - #version 460 core - layout ( location = 1 ) out vec4 a ; - layout ( location = 0 ) out vec4 b ; - void main() { a = vec4(1.0); b = vec4(0.0); } - """)); - } - - @Test - void implicitLocationZeroIsFound() { - assertEquals(0, maxOutputLocationOf(""" - #version 460 core - out vec4 fragColor; - void main() { fragColor = vec4(1.0); } - """)); - } - - @Test - void noOutputsYieldsMinusOne() { - assertEquals(-1, maxOutputLocationOf(""" - #version 460 core - void main() { } - """)); + @ParameterizedTest(name = "{0}") + @MethodSource("fragments") + void maxOutputLocation(String name, int expected, String source) { + assertEquals(expected, maxOutputLocationOf(source)); } @Test void vertexStageLeavesItUnset() { - final SpirvCompiler.Result r = SpirvCompiler.compile(""" + final ByteBuffer spirv = compileOrFail(""" #version 460 core layout ( location = 0 ) out vec4 vColor ; void main() { vColor = vec4(1.0); gl_Position = vec4(0.0); } - """, Shaderc.shaderc_vertex_shader, "out.vert", SpirvCompiler.Options.vulkanForced460Core()); - assertNotNull(r.spirv(), () -> "compile failed: " + r.error()); - final ByteBuffer spirv = r.spirv(); + """, Shaderc.shaderc_vertex_shader); try { assertEquals(-1, ShaderManager.reflectStage(spirv, true).maxOutputLocation()); } finally { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/FragmentStorageTextureReadGpuTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/FragmentStorageTextureReadGpuTest.java index 3ee04d361..99d373dee 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/FragmentStorageTextureReadGpuTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/FragmentStorageTextureReadGpuTest.java @@ -1,9 +1,11 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; +import com.gtnewhorizons.angelica.sdlgpu.RealDeviceExtension; import com.gtnewhorizons.angelica.sdlgpu.SdlTestRig; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL20; @@ -25,11 +27,10 @@ import java.nio.file.Files; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import static org.lwjgl.sdl.SDLGPU.*; +@ExtendWith(RealDeviceExtension.class) class FragmentStorageTextureReadGpuTest { private static SdlTestRig rig; @@ -40,15 +41,10 @@ class FragmentStorageTextureReadGpuTest { @BeforeAll static void setUp() throws Exception { - rig = SdlTestRig.acquireRealDevice(); + rig = RealDeviceExtension.rig(); sm = new ShaderManager(rig.device); } - @AfterAll - static void tearDown() { - SdlTestRig.releaseRealDevice(); - } - private static final String VERTEX = String.join("\n", "#version 460 core", "void main() {", @@ -254,6 +250,9 @@ class FragmentStorageTextureReadGpuTest { return file; } - @Test void fragmentImageLoadReadsCorrectTexels_noSamplers() throws IOException { runCase(0); } - @Test void fragmentImageLoadReadsCorrectTexels_twoSamplers() throws IOException { runCase(2); } + @ParameterizedTest(name = "{0} samplers") + @ValueSource(ints = {0, 2}) + void fragmentImageLoadReadsCorrectTexels(int samplerCount) throws IOException { + runCase(samplerCount); + } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/GraphicsBindingMapSsboTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/GraphicsBindingMapSsboTest.java index 0b7a2f7f0..79e8259d3 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/GraphicsBindingMapSsboTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/GraphicsBindingMapSsboTest.java @@ -1,6 +1,5 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import org.junit.jupiter.api.Test; import org.lwjgl.PointerBuffer; import org.lwjgl.opengl.GL20; @@ -14,6 +13,7 @@ import org.lwjgl.util.spvc.SpvcReflectedResource; import java.nio.ByteBuffer; import java.nio.IntBuffer; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @@ -33,7 +33,7 @@ class GraphicsBindingMapSsboTest { @Test void extractsOriginalSsboBinding() { - final ByteBuffer spirv = compile(FRAG_WITH_SSBO, Shaderc.shaderc_fragment_shader); + final ByteBuffer spirv = compileOrFail(FRAG_WITH_SSBO, Shaderc.shaderc_fragment_shader); try { final ShaderManager.GraphicsBindingMap map = ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_FRAGMENT_SHADER); assertEquals(1, map.roSsboGlSlots().length); @@ -56,7 +56,7 @@ class GraphicsBindingMapSsboTest { layout(location = 0) out vec4 o; void main() { o = texture(s0, uv); } """; - final ByteBuffer spirv = compile(src, Shaderc.shaderc_fragment_shader); + final ByteBuffer spirv = compileOrFail(src, Shaderc.shaderc_fragment_shader); try { final ShaderManager.GraphicsBindingMap map = ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_FRAGMENT_SHADER); assertEquals(0, map.roSsboGlSlots().length); @@ -97,12 +97,4 @@ class GraphicsBindingMapSsboTest { } return out; } - - private static ByteBuffer compile(String source, int shaderKind) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, shaderKind, "frag_test", SpirvCompiler.Options.vulkanForced460Core()); - if (r.spirv() == null) { - fail("frag compile failed: " + r.error()); - } - return r.spirv(); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/LogicOpLoweringTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/LogicOpLoweringTest.java index 07432d578..cc3f53565 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/LogicOpLoweringTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/LogicOpLoweringTest.java @@ -10,6 +10,7 @@ import java.nio.ByteBuffer; import java.util.List; import java.util.function.IntBinaryOperator; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -36,9 +37,7 @@ class LogicOpLoweringTest { } private static List compileAndReflectSamplers(String src) { - final SpirvCompiler.Result r = SpirvCompiler.compile(src, Shaderc.shaderc_fragment_shader, "logicop.frag", SpirvCompiler.Options.vulkanForced460Core()); - assertNotNull(r.spirv(), () -> "compile failed: " + r.error() + "\n" + src); - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(src, Shaderc.shaderc_fragment_shader); try { ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_FRAGMENT_SHADER); return ShaderManager.reflectStage(spirv, false).samplerNames(); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockInjectorTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockInjectorTest.java index 73b979edd..2316dc136 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockInjectorTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockInjectorTest.java @@ -1,6 +1,5 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import com.gtnewhorizons.angelica.glsm.hooks.PerFrameUniformBlock; import com.gtnewhorizons.angelica.glsm.shader.UniformType; import com.gtnewhorizons.angelica.glsm.hooks.PerFrameUniformBlock.Member; @@ -11,9 +10,9 @@ import java.nio.ByteBuffer; import java.util.LinkedHashMap; import java.util.List; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.lwjgl.system.MemoryUtil.memFree; @@ -101,9 +100,7 @@ class PerFrameBlockInjectorTest { assertTrue(out.contains("angelica_pfb_unused_rainStrength"), "shadowed member must be renamed, not dropped"); assertTrue(out.contains("const float rainStrength = 3.14159265359;"), "the pack's declaration must survive"); - final SpirvCompiler.Result r = SpirvCompiler.compile(out, Shaderc.shaderc_fragment_shader, "shadowed.frag", SpirvCompiler.Options.vulkanForced460Core()); - assertNotNull(r.spirv(), () -> "shadowed source failed to compile: " + r.error()); - memFree(r.spirv()); + memFree(compileOrFail(out, Shaderc.shaderc_fragment_shader)); } @Test @@ -124,9 +121,7 @@ class PerFrameBlockInjectorTest { assertFalse(out.contains("angelica_pfb_unused_cameraPosition")); assertTrue(out.contains("vec4 cameraPosition = vec4(0);"), "the local declaration must survive"); - final SpirvCompiler.Result r = SpirvCompiler.compile(out, Shaderc.shaderc_fragment_shader, "localshadow.frag", SpirvCompiler.Options.vulkanForced460Core()); - assertNotNull(r.spirv(), () -> "locally-shadowed source failed to compile: " + r.error()); - memFree(r.spirv()); + memFree(compileOrFail(out, Shaderc.shaderc_fragment_shader)); } @Test @@ -163,9 +158,7 @@ class PerFrameBlockInjectorTest { assertTrue(out.contains("angelica_pfb_unused_isRightHanded"), "its block slot must still be held"); assertFalse(out.contains(" int isRightHanded;")); - final SpirvCompiler.Result r = SpirvCompiler.compile(out, Shaderc.shaderc_fragment_shader, "booltype.frag", SpirvCompiler.Options.vulkanForced460Core()); - assertNotNull(r.spirv(), () -> "bool-typed uniform failed to compile: " + r.error()); - memFree(r.spirv()); + memFree(compileOrFail(out, Shaderc.shaderc_fragment_shader)); } @Test @@ -237,9 +230,7 @@ class PerFrameBlockInjectorTest { } private static LinkedHashMap offsetsOf(String source) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, Shaderc.shaderc_fragment_shader, "off.frag", SpirvCompiler.Options.vulkanForced460Core()); - assertNotNull(r.spirv(), () -> "compile failed: " + r.error()); - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(source, Shaderc.shaderc_fragment_shader); try { final LinkedHashMap out = new LinkedHashMap<>(); for (ShaderManager.UboMember m : ShaderManager.reflectStage(spirv, false).blocks()[ShaderManager.BLOCK_PER_FRAME].members()) { @@ -261,10 +252,7 @@ class PerFrameBlockInjectorTest { void main() { fragColor = vec4(cameraPosition, rainStrength); } """, BLOCK, null); - final SpirvCompiler.Result r = SpirvCompiler.compile(out, Shaderc.shaderc_fragment_shader, "injected.frag", SpirvCompiler.Options.vulkanForced460Core()); - assertNotNull(r.spirv(), () -> "injected source failed to compile: " + r.error()); - - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(out, Shaderc.shaderc_fragment_shader); try { final ShaderManager.StageReflection refl = ShaderManager.reflectStage(spirv, false); final List members = refl.blocks()[ShaderManager.BLOCK_PER_FRAME].members(); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockLocationRoutingTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockLocationRoutingTest.java index 0e536efb7..9f7e3897e 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockLocationRoutingTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockLocationRoutingTest.java @@ -3,13 +3,13 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; import com.gtnewhorizons.angelica.glsm.hooks.PerFrameUniformBlock; import com.gtnewhorizons.angelica.glsm.shader.UniformType; import com.gtnewhorizons.angelica.glsm.hooks.PerFrameUniformBlock.Member; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import org.junit.jupiter.api.Test; import org.lwjgl.util.shaderc.Shaderc; import java.nio.ByteBuffer; import java.util.List; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -39,10 +39,7 @@ class PerFrameBlockLocationRoutingTest { private static ShaderManager.ProgramObject linkedProgram() { final String injected = ShaderTransformChain.inject(SOURCE, PER_FRAME, PER_PASS); - final SpirvCompiler.Result r = SpirvCompiler.compile(injected, Shaderc.shaderc_fragment_shader, "routing.frag", SpirvCompiler.Options.vulkanForced460Core()); - assertNotNull(r.spirv(), () -> "compile failed: " + r.error()); - - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(injected, Shaderc.shaderc_fragment_shader); try { final ShaderManager.StageReflection refl = ShaderManager.reflectStage(spirv, false); final ShaderManager.ProgramObject prog = new ShaderManager.ProgramObject(); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockReadOnlyTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockReadOnlyTest.java index 20e4570fc..318de310f 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockReadOnlyTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockReadOnlyTest.java @@ -3,14 +3,13 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; import com.gtnewhorizons.angelica.glsm.hooks.PerFrameUniformBlock; import com.gtnewhorizons.angelica.glsm.shader.UniformType; import com.gtnewhorizons.angelica.glsm.hooks.PerFrameUniformBlock.Member; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import org.junit.jupiter.api.Test; import org.lwjgl.util.shaderc.Shaderc; import java.nio.ByteBuffer; import java.util.List; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.lwjgl.system.MemoryUtil.memFree; @@ -31,10 +30,7 @@ class PerFrameBlockReadOnlyTest { @Test void injectedBlocksAreDecoratedNonWritable() { final String injected = ShaderTransformChain.inject(DUAL_SOURCE, BLOCK, PER_PASS); - final SpirvCompiler.Result r = SpirvCompiler.compile(injected, Shaderc.shaderc_fragment_shader, "readonly.frag", SpirvCompiler.Options.vulkanForced460Core()); - assertNotNull(r.spirv(), () -> "compile failed: " + r.error()); - - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(injected, Shaderc.shaderc_fragment_shader); try { final ShaderManager.StageReflection refl = ShaderManager.reflectStage(spirv, false); for (int b = 0; b < ShaderManager.BLOCK_COUNT; b++) { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockReflectionTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockReflectionTest.java index 78e09366a..0d3eba9b7 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockReflectionTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PerFrameBlockReflectionTest.java @@ -1,6 +1,5 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import org.junit.jupiter.api.Test; import org.lwjgl.util.shaderc.Shaderc; @@ -8,8 +7,8 @@ import java.nio.ByteBuffer; import java.util.LinkedHashMap; import java.util.Map; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.lwjgl.system.MemoryUtil.memFree; class PerFrameBlockReflectionTest { @@ -39,7 +38,6 @@ class PerFrameBlockReflectionTest { assertEquals(96, offsets.get("rainStrength")); } - @Test void offsetsAreIdenticalAcrossProgramsReferencingDifferentMembers() { assertEquals(perFrameOffsetsOf(shaderUsing("vec4(cameraPosition, 1.0)")), perFrameOffsetsOf(shaderUsing("vec4(rainStrength)")), "a shader touching only the last member must still see every member at the same offset"); @@ -94,9 +92,7 @@ class PerFrameBlockReflectionTest { } private static ShaderManager.StageReflection reflect(String source) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, Shaderc.shaderc_fragment_shader, "test.frag", SpirvCompiler.Options.vulkanForced460Core()); - assertNotNull(r.spirv(), () -> "shaderc failed: " + r.error()); - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(source, Shaderc.shaderc_fragment_shader); try { return ShaderManager.reflectStage(spirv, false); } finally { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PrewarmSingleParseEquivalenceTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PrewarmSingleParseEquivalenceTest.java index c51a50d87..47f8c1017 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PrewarmSingleParseEquivalenceTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PrewarmSingleParseEquivalenceTest.java @@ -6,12 +6,15 @@ import com.gtnewhorizons.angelica.glsm.hooks.PerFrameUniformBlock.Member; import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess; import com.gtnewhorizons.angelica.glsm.shader.UniformType; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL43; import java.util.List; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -23,153 +26,6 @@ class PrewarmSingleParseEquivalenceTest { GLSMHooks.perPassUniformBlock = null; } - private static void assertEquivalent(String label, String source, int glShaderType) { - GlslVulkanPreprocess.clearCache(); - final String legacyOut = ShaderTransformChain.run(source, glShaderType); - GlslVulkanPreprocess.clearCache(); - final String parseOnceOut = ShaderManager.applyPrewarmTransformsFull(source, glShaderType, GLSMHooks.perFrameUniformBlock, GLSMHooks.perPassUniformBlock).source(); - assertEquals(legacyOut, parseOnceOut, "single-parse vs chained pipeline diverged for " + label); - } - - private static void assertEquivalentIgnoringWhitespace(String label, String source, int glShaderType) { - GlslVulkanPreprocess.clearCache(); - final String legacyOut = ShaderTransformChain.run(source, glShaderType); - GlslVulkanPreprocess.clearCache(); - final String parseOnceOut = ShaderManager.applyPrewarmTransformsFull(source, glShaderType, GLSMHooks.perFrameUniformBlock, GLSMHooks.perPassUniformBlock).source(); - assertEquals(legacyOut.replaceAll("\\s+", " "), parseOnceOut.replaceAll("\\s+", " "), "single-parse vs chained pipeline diverged beyond whitespace for " + label); - } - - @Test - void minimalFragment_noTransformsNeeded() { - final String src = "#version 460 core\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() { fragColor = vec4(1.0); }\n"; - assertEquivalent("minimal-fragment", src, GL20.GL_FRAGMENT_SHADER); - } - - @Test - void minimalVertex_depthRemapInjected() { - final String src = "#version 460 core\n" - + "layout(location = 0) in vec3 a_Position;\n" - + "void main() { gl_Position = vec4(a_Position, 1.0); }\n"; - assertEquivalent("minimal-vertex", src, GL20.GL_VERTEX_SHADER); - } - - @Test - void fragmentWithUnusedSampler_strippedAndIdsRewritten() { - final String src = "#version 460 core\n" - + "uniform sampler2D u_Unused;\n" - + "uniform sampler2D u_Used;\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() {\n" - + " fragColor = texture(u_Used, vec2(0.5));\n" - + "}\n"; - assertEquivalent("frag-unused-sampler", src, GL20.GL_FRAGMENT_SHADER); - } - - @Test - void vertexWithUnlocatedInputs_layoutLocationsInserted() { - final String src = "#version 460 core\n" - + "layout(location = 0) in vec3 a_Position;\n" - + "in vec2 a_TexCoord;\n" - + "in vec3 a_Normal;\n" - + "out vec2 v_TexCoord;\n" - + "void main() {\n" - + " v_TexCoord = a_TexCoord;\n" - + " gl_Position = vec4(a_Position + a_Normal, 1.0);\n" - + "}\n"; - assertEquivalent("vertex-unlocated-inputs", src, GL20.GL_VERTEX_SHADER); - } - - @Test - void vertexWithBuiltinIdRewrites() { - final String src = "#version 460 core\n" - + "void main() {\n" - + " int v = gl_VertexID;\n" - + " int i = gl_InstanceID;\n" - + " gl_Position = vec4(float(v + i), 0.0, 0.0, 1.0);\n" - + "}\n"; - assertEquivalent("vertex-builtin-ids", src, GL20.GL_VERTEX_SHADER); - } - - @Test - void fragmentWithBoolUniformsAndSamplerRename() { - final String src = "#version 460 core\n" - + "uniform bool u_Enabled;\n" - + "uniform float u_Gain = 1.5;\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() {\n" - + " fragColor = vec4(u_Enabled ? u_Gain : 0.0);\n" - + "}\n"; - assertEquivalent("frag-bool-uniform-init", src, GL20.GL_FRAGMENT_SHADER); - } - - @Test - void samplerOnlyReferencedInsidePreprocessorBlock_preservedConsistently() { - final String src = "#version 460 core\n" - + "#define FOO 1\n" - + "layout(binding = 0) uniform sampler2D u_S;\n" - + "void main() {\n" - + "#if FOO\n" - + " float a = textureLod(u_S, vec2(0), 0.0).r;\n" - + "#endif\n" - + "}\n"; - assertEquivalent("sampler-in-preprocessor-block", src, GL20.GL_FRAGMENT_SHADER); - } - - @Test - void compute_noVertexOnlyPasses() { - final String src = "#version 460 core\n" - + "layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;\n" - + "uniform sampler2D u_Unused;\n" - + "void main() {}\n"; - assertEquivalent("compute-empty", src, GL43.GL_COMPUTE_SHADER); - } - - @Test - void geometry_noVertexOnlyPasses() { - final String src = "#version 460 core\n" - + "layout(triangles) in;\n" - + "layout(triangle_strip, max_vertices = 3) out;\n" - + "void main() {\n" - + " for (int i = 0; i < 3; i++) {\n" - + " gl_Position = gl_in[i].gl_Position;\n" - + " EmitVertex();\n" - + " }\n" - + " EndPrimitive();\n" - + "}\n"; - assertEquivalent("geometry-passthrough", src, GL32.GL_GEOMETRY_SHADER); - } - - @Test - void readOnlyImage_rewrittenToTextureWithSamplerlessExtension() { - final String src = "#version 460 core\n" - + "layout(binding = 2, rgba8) uniform readonly image2D u_Img;\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() {\n" - + " fragColor = imageLoad(u_Img, ivec2(1, 2)) + vec4(imageSize(u_Img), 0.0, 1.0);\n" - + "}\n"; - assertEquivalent("readonly-image", src, GL20.GL_FRAGMENT_SHADER); - } - - @Test - void samplerlessTexture_extensionInserted() { - final String src = "#version 460 core\n" - + "uniform texture2D u_Tex;\n" - + "uniform sampler u_Smp;\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() {\n" - + " fragColor = texelFetch(u_Tex, ivec2(0), 0) + texture(sampler2D(u_Tex, u_Smp), vec2(0.5));\n" - + "}\n"; - assertEquivalent("samplerless-texture", src, GL20.GL_FRAGMENT_SHADER); - } - - @Test - void unparseable_fallsBackToInputUnchanged() { - final String src = "this is not valid GLSL @@@@\n"; - assertEquivalent("unparseable", src, GL20.GL_FRAGMENT_SHADER); - } - private static final PerFrameUniformBlock PER_FRAME = new PerFrameUniformBlock(List.of( new Member("cameraPosition", UniformType.VEC3), new Member("frameTimeCounter", UniformType.FLOAT))); @@ -177,79 +33,157 @@ class PrewarmSingleParseEquivalenceTest { private static final PerFrameUniformBlock PER_PASS = new PerFrameUniformBlock(List.of( new Member("alphaTestRef", UniformType.FLOAT))); - @Test - void perFrameBlock_replaceableUniformStrippedAndInjected() { - GLSMHooks.perFrameUniformBlock = PER_FRAME; - GLSMHooks.perPassUniformBlock = PER_PASS; - final String src = "#version 460 core\n" - + "uniform float frameTimeCounter;\n" - + "uniform float alphaTestRef;\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() {\n" - + " if (fragColor.a < alphaTestRef) discard;\n" - + " fragColor = vec4(frameTimeCounter);\n" - + "}\n"; - assertEquivalent("pfb-replaceable", src, GL20.GL_FRAGMENT_SHADER); + private static Arguments shader(String name, int glShaderType, PerFrameUniformBlock perFrame, PerFrameUniformBlock perPass, boolean exact, String source) { + return Arguments.of(name, glShaderType, perFrame, perPass, exact, source); } - @Test - void perFrameBlock_firstDeclIsUnlocatedVsInput() { - GLSMHooks.perFrameUniformBlock = PER_FRAME; - final String src = "#version 460 core\n" - + "in vec3 a_Position;\n" - + "layout(location = 5) in vec2 a_TexCoord;\n" - + "uniform vec3 cameraPosition;\n" - + "void main() {\n" - + " gl_Position = vec4(a_Position - cameraPosition, 1.0);\n" - + "}\n"; - assertEquivalent("pfb-unlocated-first-in", src, GL20.GL_VERTEX_SHADER); + static Stream shaders() { + return Stream.of( + shader("minimalFragment_noTransformsNeeded", GL20.GL_FRAGMENT_SHADER, null, null, true, + "#version 460 core\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() { fragColor = vec4(1.0); }\n"), + shader("minimalVertex_depthRemapInjected", GL20.GL_VERTEX_SHADER, null, null, true, + "#version 460 core\n" + + "layout(location = 0) in vec3 a_Position;\n" + + "void main() { gl_Position = vec4(a_Position, 1.0); }\n"), + shader("fragmentWithUnusedSampler_strippedAndIdsRewritten", GL20.GL_FRAGMENT_SHADER, null, null, true, + "#version 460 core\n" + + "uniform sampler2D u_Unused;\n" + + "uniform sampler2D u_Used;\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() {\n" + + " fragColor = texture(u_Used, vec2(0.5));\n" + + "}\n"), + shader("vertexWithUnlocatedInputs_layoutLocationsInserted", GL20.GL_VERTEX_SHADER, null, null, true, + "#version 460 core\n" + + "layout(location = 0) in vec3 a_Position;\n" + + "in vec2 a_TexCoord;\n" + + "in vec3 a_Normal;\n" + + "out vec2 v_TexCoord;\n" + + "void main() {\n" + + " v_TexCoord = a_TexCoord;\n" + + " gl_Position = vec4(a_Position + a_Normal, 1.0);\n" + + "}\n"), + shader("vertexWithBuiltinIdRewrites", GL20.GL_VERTEX_SHADER, null, null, true, + "#version 460 core\n" + + "void main() {\n" + + " int v = gl_VertexID;\n" + + " int i = gl_InstanceID;\n" + + " gl_Position = vec4(float(v + i), 0.0, 0.0, 1.0);\n" + + "}\n"), + shader("fragmentWithBoolUniformsAndSamplerRename", GL20.GL_FRAGMENT_SHADER, null, null, true, + "#version 460 core\n" + + "uniform bool u_Enabled;\n" + + "uniform float u_Gain = 1.5;\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() {\n" + + " fragColor = vec4(u_Enabled ? u_Gain : 0.0);\n" + + "}\n"), + shader("samplerOnlyReferencedInsidePreprocessorBlock_preservedConsistently", GL20.GL_FRAGMENT_SHADER, null, null, true, + "#version 460 core\n" + + "#define FOO 1\n" + + "layout(binding = 0) uniform sampler2D u_S;\n" + + "void main() {\n" + + "#if FOO\n" + + " float a = textureLod(u_S, vec2(0), 0.0).r;\n" + + "#endif\n" + + "}\n"), + shader("compute_noVertexOnlyPasses", GL43.GL_COMPUTE_SHADER, null, null, true, + "#version 460 core\n" + + "layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;\n" + + "uniform sampler2D u_Unused;\n" + + "void main() {}\n"), + shader("geometry_noVertexOnlyPasses", GL32.GL_GEOMETRY_SHADER, null, null, true, + "#version 460 core\n" + + "layout(triangles) in;\n" + + "layout(triangle_strip, max_vertices = 3) out;\n" + + "void main() {\n" + + " for (int i = 0; i < 3; i++) {\n" + + " gl_Position = gl_in[i].gl_Position;\n" + + " EmitVertex();\n" + + " }\n" + + " EndPrimitive();\n" + + "}\n"), + shader("readOnlyImage_rewrittenToTextureWithSamplerlessExtension", GL20.GL_FRAGMENT_SHADER, null, null, true, + "#version 460 core\n" + + "layout(binding = 2, rgba8) uniform readonly image2D u_Img;\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() {\n" + + " fragColor = imageLoad(u_Img, ivec2(1, 2)) + vec4(imageSize(u_Img), 0.0, 1.0);\n" + + "}\n"), + shader("samplerlessTexture_extensionInserted", GL20.GL_FRAGMENT_SHADER, null, null, true, + "#version 460 core\n" + + "uniform texture2D u_Tex;\n" + + "uniform sampler u_Smp;\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() {\n" + + " fragColor = texelFetch(u_Tex, ivec2(0), 0) + texture(sampler2D(u_Tex, u_Smp), vec2(0.5));\n" + + "}\n"), + shader("unparseable_fallsBackToInputUnchanged", GL20.GL_FRAGMENT_SHADER, null, null, true, + "this is not valid GLSL @@@@\n"), + shader("perFrameBlock_replaceableUniformStrippedAndInjected", GL20.GL_FRAGMENT_SHADER, PER_FRAME, PER_PASS, true, + "#version 460 core\n" + + "uniform float frameTimeCounter;\n" + + "uniform float alphaTestRef;\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() {\n" + + " if (fragColor.a < alphaTestRef) discard;\n" + + " fragColor = vec4(frameTimeCounter);\n" + + "}\n"), + shader("perFrameBlock_firstDeclIsUnlocatedVsInput", GL20.GL_VERTEX_SHADER, PER_FRAME, null, true, + "#version 460 core\n" + + "in vec3 a_Position;\n" + + "layout(location = 5) in vec2 a_TexCoord;\n" + + "uniform vec3 cameraPosition;\n" + + "void main() {\n" + + " gl_Position = vec4(a_Position - cameraPosition, 1.0);\n" + + "}\n"), + shader("perFrameBlock_memberUniformWithInitializer", GL20.GL_FRAGMENT_SHADER, null, PER_PASS, true, + "#version 460 core\n" + + "uniform float alphaTestRef = 0.1;\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() {\n" + + " if (fragColor.a < alphaTestRef) discard;\n" + + "}\n"), + shader("perFrameBlock_firstDeclIsStrippedSampler", GL20.GL_FRAGMENT_SHADER, PER_FRAME, null, false, + "#version 460 core\n" + + "uniform sampler2D u_Unused;\n" + + "uniform float frameTimeCounter;\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() {\n" + + " fragColor = vec4(frameTimeCounter);\n" + + "}\n"), + shader("perFrameBlock_shadowedMemberKeepsLocalDeclaration", GL20.GL_FRAGMENT_SHADER, PER_FRAME, null, true, + "#version 460 core\n" + + "uniform vec4 frameTimeCounter;\n" + + "uniform vec3 cameraPosition;\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() {\n" + + " fragColor = frameTimeCounter + vec4(cameraPosition, 0.0);\n" + + "}\n"), + shader("perFrameBlock_noReferencesLeavesSourceUntouched", GL20.GL_FRAGMENT_SHADER, PER_FRAME, PER_PASS, true, + "#version 460 core\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() { fragColor = vec4(1.0); }\n")); } - @Test - void perFrameBlock_memberUniformWithInitializer() { - GLSMHooks.perPassUniformBlock = PER_PASS; - final String src = "#version 460 core\n" - + "uniform float alphaTestRef = 0.1;\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() {\n" - + " if (fragColor.a < alphaTestRef) discard;\n" - + "}\n"; - assertEquivalent("pfb-initializer", src, GL20.GL_FRAGMENT_SHADER); - } + /** {@code exact == false} compares modulo whitespace. */ + @ParameterizedTest(name = "{0}") + @MethodSource("shaders") + void singleParseMatchesChainedPipeline(String name, int glShaderType, PerFrameUniformBlock perFrame, PerFrameUniformBlock perPass, boolean exact, String source) { + GLSMHooks.perFrameUniformBlock = perFrame; + GLSMHooks.perPassUniformBlock = perPass; - @Test - void perFrameBlock_firstDeclIsStrippedSampler() { - GLSMHooks.perFrameUniformBlock = PER_FRAME; - final String src = "#version 460 core\n" - + "uniform sampler2D u_Unused;\n" - + "uniform float frameTimeCounter;\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() {\n" - + " fragColor = vec4(frameTimeCounter);\n" - + "}\n"; - assertEquivalentIgnoringWhitespace("pfb-stripped-first-sampler", src, GL20.GL_FRAGMENT_SHADER); - } + GlslVulkanPreprocess.clearCache(); + final String legacyOut = ShaderTransformChain.run(source, glShaderType); + GlslVulkanPreprocess.clearCache(); + final String parseOnceOut = ShaderManager.applyPrewarmTransformsFull(source, glShaderType, GLSMHooks.perFrameUniformBlock, GLSMHooks.perPassUniformBlock).source(); - @Test - void perFrameBlock_shadowedMemberKeepsLocalDeclaration() { - GLSMHooks.perFrameUniformBlock = PER_FRAME; - final String src = "#version 460 core\n" - + "uniform vec4 frameTimeCounter;\n" - + "uniform vec3 cameraPosition;\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() {\n" - + " fragColor = frameTimeCounter + vec4(cameraPosition, 0.0);\n" - + "}\n"; - assertEquivalent("pfb-shadowed", src, GL20.GL_FRAGMENT_SHADER); - } - - @Test - void perFrameBlock_noReferencesLeavesSourceUntouched() { - GLSMHooks.perFrameUniformBlock = PER_FRAME; - GLSMHooks.perPassUniformBlock = PER_PASS; - final String src = "#version 460 core\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() { fragColor = vec4(1.0); }\n"; - assertEquivalent("pfb-unreferenced", src, GL20.GL_FRAGMENT_SHADER); + if (exact) { + assertEquals(legacyOut, parseOnceOut, "single-parse vs chained pipeline diverged for " + name); + } else { + assertEquals(legacyOut.replaceAll("\\s+", " "), parseOnceOut.replaceAll("\\s+", " "), "single-parse vs chained pipeline diverged beyond whitespace for " + name); + } } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PrewarmTreeHandoffEquivalenceTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PrewarmTreeHandoffEquivalenceTest.java index 7bc8413a8..2d2a08606 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PrewarmTreeHandoffEquivalenceTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/PrewarmTreeHandoffEquivalenceTest.java @@ -7,13 +7,16 @@ import com.gtnewhorizons.angelica.glsm.hooks.PerFrameUniformBlock.Member; import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess; import com.gtnewhorizons.angelica.glsm.shader.UniformType; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL32; import org.taumc.glsl.ShaderParser; import org.taumc.glsl.grammar.GLSLParser; import java.util.List; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -27,7 +30,70 @@ class PrewarmTreeHandoffEquivalenceTest { GLSMHooks.perPassUniformBlock = null; } - private static void assertHandoffEquivalent(String label, String body, int glShaderType) { + private static Arguments shader(String name, int glShaderType, PerFrameUniformBlock perFrame, String body) { + return Arguments.of(name, glShaderType, perFrame, body); + } + + static Stream shaders() { + return Stream.of( + shader("minimalFragment", GL20.GL_FRAGMENT_SHADER, null, + "layout(location = 0) out vec4 fragColor;\n" + + "void main() { fragColor = vec4(1.0); }\n"), + shader("unusedSamplerStripped", GL20.GL_FRAGMENT_SHADER, null, + "uniform sampler2D u_Unused;\n" + + "uniform sampler2D u_Used;\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() { fragColor = texture(u_Used, vec2(0.5)); }\n"), + shader("perFrameBlockInjection", GL20.GL_FRAGMENT_SHADER, new PerFrameUniformBlock(List.of( + new Member("cameraPosition", UniformType.VEC3), + new Member("frameTimeCounter", UniformType.FLOAT))), + "uniform float frameTimeCounter;\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() { fragColor = vec4(frameTimeCounter); }\n"), + shader("vertexClipZAndUnlocatedInputs", GL20.GL_VERTEX_SHADER, new PerFrameUniformBlock(List.of( + new Member("cameraPosition", UniformType.VEC3))), + "layout(location = 0) in vec3 a_Position;\n" + + "in vec2 a_TexCoord;\n" + + "uniform vec3 cameraPosition;\n" + + "out vec2 v_TexCoord;\n" + + "void main() {\n" + + " v_TexCoord = a_TexCoord;\n" + + " gl_Position = vec4(a_Position - cameraPosition, 1.0);\n" + + "}\n"), + shader("boolUniformMetadata", GL20.GL_FRAGMENT_SHADER, null, + "uniform bool u_Enabled;\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() { fragColor = vec4(u_Enabled ? 1.0 : 0.0); }\n"), + shader("texelFetchPath", GL20.GL_FRAGMENT_SHADER, null, + "uniform sampler2D u_Data;\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "void main() { fragColor = texelFetch(u_Data, ivec2(0), 0); }\n"), + shader("cppKeywordLocal", GL20.GL_FRAGMENT_SHADER, null, + "layout(location = 0) in vec4 v_Color;\n" + + "layout(location = 0) out vec4 fragColor;\n" + + "vec4 trace(vec4 c) {\n" + + " vec4 try = c * 0.5;\n" + + " if (try.a > -0.5) return try;\n" + + " return c;\n" + + "}\n" + + "void main() { fragColor = trace(v_Color); }\n"), + shader("geometryStage", GL32.GL_GEOMETRY_SHADER, null, + "layout(triangles) in;\n" + + "layout(triangle_strip, max_vertices = 3) out;\n" + + "void main() {\n" + + " for (int i = 0; i < 3; i++) {\n" + + " gl_Position = gl_in[i].gl_Position;\n" + + " EmitVertex();\n" + + " }\n" + + " EndPrimitive();\n" + + "}\n")); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("shaders") + void treeHandoffMatchesReparse(String label, int glShaderType, PerFrameUniformBlock perFrame, String body) { + GLSMHooks.perFrameUniformBlock = perFrame; + final GLSLParser.Translation_unitContext tree = ShaderParser.parseShader(body).full(); GlslTransformUtils.restoreReservedWordsInTree(tree); final String printedBody = GlslTransformUtils.getFormattedShaderRebased(tree, ""); @@ -41,97 +107,4 @@ class PrewarmTreeHandoffEquivalenceTest { assertEquals(reparse.source(), treeFed.source(), "tree handoff diverged from re-parse for " + label); assertEquals(reparse.boolUniforms(), treeFed.boolUniforms(), "bool uniforms diverged for " + label); } - - @Test - void minimalFragment() { - assertHandoffEquivalent("minimal-fragment", - "layout(location = 0) out vec4 fragColor;\n" - + "void main() { fragColor = vec4(1.0); }\n", - GL20.GL_FRAGMENT_SHADER); - } - - @Test - void unusedSamplerStripped() { - assertHandoffEquivalent("unused-sampler", - "uniform sampler2D u_Unused;\n" - + "uniform sampler2D u_Used;\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() { fragColor = texture(u_Used, vec2(0.5)); }\n", - GL20.GL_FRAGMENT_SHADER); - } - - @Test - void perFrameBlockInjection() { - GLSMHooks.perFrameUniformBlock = new PerFrameUniformBlock(List.of( - new Member("cameraPosition", UniformType.VEC3), - new Member("frameTimeCounter", UniformType.FLOAT))); - assertHandoffEquivalent("per-frame-block", - "uniform float frameTimeCounter;\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() { fragColor = vec4(frameTimeCounter); }\n", - GL20.GL_FRAGMENT_SHADER); - } - - @Test - void vertexClipZAndUnlocatedInputs() { - GLSMHooks.perFrameUniformBlock = new PerFrameUniformBlock(List.of( - new Member("cameraPosition", UniformType.VEC3))); - assertHandoffEquivalent("vertex-clipz-unlocated", - "layout(location = 0) in vec3 a_Position;\n" - + "in vec2 a_TexCoord;\n" - + "uniform vec3 cameraPosition;\n" - + "out vec2 v_TexCoord;\n" - + "void main() {\n" - + " v_TexCoord = a_TexCoord;\n" - + " gl_Position = vec4(a_Position - cameraPosition, 1.0);\n" - + "}\n", - GL20.GL_VERTEX_SHADER); - } - - @Test - void boolUniformMetadata() { - assertHandoffEquivalent("bool-uniform", - "uniform bool u_Enabled;\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() { fragColor = vec4(u_Enabled ? 1.0 : 0.0); }\n", - GL20.GL_FRAGMENT_SHADER); - } - - @Test - void texelFetchPath() { - assertHandoffEquivalent("texelfetch", - "uniform sampler2D u_Data;\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "void main() { fragColor = texelFetch(u_Data, ivec2(0), 0); }\n", - GL20.GL_FRAGMENT_SHADER); - } - - @Test - void cppKeywordLocal() { - assertHandoffEquivalent("cpp-keyword-local", - "layout(location = 0) in vec4 v_Color;\n" - + "layout(location = 0) out vec4 fragColor;\n" - + "vec4 trace(vec4 c) {\n" - + " vec4 try = c * 0.5;\n" - + " if (try.a > -0.5) return try;\n" - + " return c;\n" - + "}\n" - + "void main() { fragColor = trace(v_Color); }\n", - GL20.GL_FRAGMENT_SHADER); - } - - @Test - void geometryStage() { - assertHandoffEquivalent("geometry", - "layout(triangles) in;\n" - + "layout(triangle_strip, max_vertices = 3) out;\n" - + "void main() {\n" - + " for (int i = 0; i < 3; i++) {\n" - + " gl_Position = gl_in[i].gl_Position;\n" - + " EmitVertex();\n" - + " }\n" - + " EndPrimitive();\n" - + "}\n", - GL32.GL_GEOMETRY_SHADER); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ReadOnlyStorageTextureDescriptorTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ReadOnlyStorageTextureDescriptorTest.java index e5a7250e7..12cf9aebf 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ReadOnlyStorageTextureDescriptorTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ReadOnlyStorageTextureDescriptorTest.java @@ -1,7 +1,6 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import org.junit.jupiter.api.Test; import org.lwjgl.PointerBuffer; import org.lwjgl.opengl.GL20; @@ -19,10 +18,10 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import static org.lwjgl.system.MemoryStack.stackPush; class ReadOnlyStorageTextureDescriptorTest { @@ -45,9 +44,7 @@ class ReadOnlyStorageTextureDescriptorTest { private static ByteBuffer compileRewritten() { final String source = preprocess(FRAGMENT_READONLY_IMAGE, GL20.GL_FRAGMENT_SHADER); - final SpirvCompiler.Result r = SpirvCompiler.compile(source, Shaderc.shaderc_fragment_shader, "readonly-storage-test", SpirvCompiler.Options.vulkanForced460Core()); - if (r.spirv() == null) fail("compile failed: " + r.error() + "\nsource:\n" + source); - return r.spirv(); + return compileOrFail(source, Shaderc.shaderc_fragment_shader); } private static Map bindingsOf(ByteBuffer spirv, int resourceType) { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SDLGPUUniformPackingTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SDLGPUUniformPackingTest.java index 3a83e0638..0aeecb73d 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SDLGPUUniformPackingTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SDLGPUUniformPackingTest.java @@ -1,9 +1,13 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.system.MemoryUtil; import java.nio.FloatBuffer; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -19,32 +23,31 @@ class SDLGPUUniformPackingTest { assertEquals(expected, actual, 1e-6f, "mismatch at " + at); } - private static void assertEq(float expected, float actual) { - assertEquals(expected, actual, 1e-6f); + private static Arguments matrix(String name, int dim, boolean transpose, float... expected) { + return Arguments.of(name, dim, transpose, expected); } - @Test - void mat3_columnMajor_copiedVerbatim() { - final FloatBuffer src = wrap(1, 2, 3, 4, 5, 6, 7, 8, 9); - try { - final float[] dst = new float[9]; - MatrixMarshal.marshalMatrixToColumnMajor(src, 0, 1, 3, false, dst, 0); - for (int i = 0; i < 9; i++) assertEq(i + 1, dst[i], "[" + i + "]"); - } finally { - MemoryUtil.memFree(src); - } + /** Source is always 1..dim*dim. */ + static Stream matrices() { + return Stream.of( + matrix("mat2 column-major: verbatim", 2, false, 1, 2, 3, 4), + matrix("mat2 row-major: transposed", 2, true, 1, 3, 2, 4), + matrix("mat3 column-major: verbatim", 3, false, 1, 2, 3, 4, 5, 6, 7, 8, 9), + matrix("mat3 row-major: transposed", 3, true, 1, 4, 7, 2, 5, 8, 3, 6, 9), + matrix("mat4 column-major: verbatim", 4, false, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16), + matrix("mat4 row-major: transposed", 4, true, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16)); } - @Test - void mat3_rowMajor_transposeFlipsToColumnMajor() { - // Input rows (1,2,3) (4,5,6) (7,8,9); output cols (1,4,7) (2,5,8) (3,6,9). - final FloatBuffer src = wrap(1, 2, 3, 4, 5, 6, 7, 8, 9); + @ParameterizedTest(name = "{0}") + @MethodSource("matrices") + void singleMatrixMarshalsToColumnMajor(String name, int dim, boolean transpose, float[] expected) { + final float[] values = new float[dim * dim]; + for (int i = 0; i < values.length; i++) values[i] = i + 1; + final FloatBuffer src = wrap(values); try { - final float[] dst = new float[9]; - MatrixMarshal.marshalMatrixToColumnMajor(src, 0, 1, 3, true, dst, 0); - assertEq(1f, dst[0], "c0.x"); assertEq(4f, dst[1], "c0.y"); assertEq(7f, dst[2], "c0.z"); - assertEq(2f, dst[3], "c1.x"); assertEq(5f, dst[4], "c1.y"); assertEq(8f, dst[5], "c1.z"); - assertEq(3f, dst[6], "c2.x"); assertEq(6f, dst[7], "c2.y"); assertEq(9f, dst[8], "c2.z"); + final float[] dst = new float[dim * dim]; + MatrixMarshal.marshalMatrixToColumnMajor(src, 0, 1, dim, transpose, dst, 0); + for (int i = 0; i < dst.length; i++) assertEq(expected[i], dst[i], "[" + i + "]"); } finally { MemoryUtil.memFree(src); } @@ -77,57 +80,4 @@ class SDLGPUUniformPackingTest { MemoryUtil.memFree(src); } } - - @Test - void mat2_columnMajor_copiedVerbatim() { - final FloatBuffer src = wrap(1, 2, 3, 4); - try { - final float[] dst = new float[4]; - MatrixMarshal.marshalMatrixToColumnMajor(src, 0, 1, 2, false, dst, 0); - assertEq(1f, dst[0], "c0.x"); assertEq(2f, dst[1], "c0.y"); - assertEq(3f, dst[2], "c1.x"); assertEq(4f, dst[3], "c1.y"); - } finally { - MemoryUtil.memFree(src); - } - } - - @Test - void mat2_rowMajor_transposeFlipsToColumnMajor() { - final FloatBuffer src = wrap(1, 2, 3, 4); - try { - final float[] dst = new float[4]; - MatrixMarshal.marshalMatrixToColumnMajor(src, 0, 1, 2, true, dst, 0); - assertEq(1f, dst[0]); assertEq(3f, dst[1]); - assertEq(2f, dst[2]); assertEq(4f, dst[3]); - } finally { - MemoryUtil.memFree(src); - } - } - - @Test - void mat4_rowMajor_transposeFlipsToColumnMajor() { - final FloatBuffer src = wrap(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); - try { - final float[] dst = new float[16]; - MatrixMarshal.marshalMatrixToColumnMajor(src, 0, 1, 4, true, dst, 0); - assertEq(1f, dst[0]); assertEq(5f, dst[1]); assertEq(9f, dst[2]); assertEq(13f, dst[3]); - assertEq(2f, dst[4]); assertEq(6f, dst[5]); assertEq(10f, dst[6]); assertEq(14f, dst[7]); - assertEq(3f, dst[8]); assertEq(7f, dst[9]); assertEq(11f, dst[10]); assertEq(15f, dst[11]); - assertEq(4f, dst[12]); assertEq(8f, dst[13]); assertEq(12f, dst[14]); assertEq(16f, dst[15]); - } finally { - MemoryUtil.memFree(src); - } - } - - @Test - void mat4_columnMajor_copiedVerbatim() { - final FloatBuffer src = wrap(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); - try { - final float[] dst = new float[16]; - MatrixMarshal.marshalMatrixToColumnMajor(src, 0, 1, 4, false, dst, 0); - for (int i = 0; i < 16; i++) assertEq(i + 1, dst[i], "[" + i + "]"); - } finally { - MemoryUtil.memFree(src); - } - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SdlSamplerStripperPreprocessorTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SdlSamplerStripperPreprocessorTest.java index 1e99c2c7f..9d1f4fffa 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SdlSamplerStripperPreprocessorTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SdlSamplerStripperPreprocessorTest.java @@ -1,6 +1,11 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; @@ -8,101 +13,99 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class SdlSamplerStripperPreprocessorTest { - @Test - void samplerReferencedOnlyInsideIfBlock_isPreserved() { - final String src = "#version 460 core\n" - + "#define FOO 1\n" - + "layout(binding = 0) uniform sampler2D u_S;\n" - + "void main() {\n" - + "#if FOO\n" - + " float a = textureLod(u_S, vec2(0), 0.0).r;\n" - + "#endif\n" - + "}\n"; - final String out = ShaderTransformChain.stripUnused(src); - assertTrue(out.contains("uniform sampler2D u_S"), "ref inside #if must keep the decl; output:\n" + out); + static Stream referenced() { + return Stream.of( + Arguments.of("samplerReferencedOnlyInsideIfBlock_isPreserved", "uniform sampler2D u_S", + "#version 460 core\n" + + "#define FOO 1\n" + + "layout(binding = 0) uniform sampler2D u_S;\n" + + "void main() {\n" + + "#if FOO\n" + + " float a = textureLod(u_S, vec2(0), 0.0).r;\n" + + "#endif\n" + + "}\n"), + Arguments.of("samplerReferencedUnconditionally_isPreserved", "uniform sampler2D u_S", + "#version 460 core\n" + + "#define FOO 1\n" + + "layout(binding = 0) uniform sampler2D u_S;\n" + + "void main() {\n" + + " float t = textureLod(u_S, vec2(0), 0.0).r;\n" + + "#if FOO\n" + + " float a = t * 2.0;\n" + + "#endif\n" + + "}\n"), + Arguments.of("samplerReferencedInBothIfAndElseBranches_isPreserved", "uniform sampler2D u_S", + "#version 460 core\n" + + "#define FOO 0\n" + + "layout(binding = 0) uniform sampler2D u_S;\n" + + "void main() {\n" + + "#if FOO\n" + + " float a = textureLod(u_S, vec2(0), 0.0).r;\n" + + "#else\n" + + " float b = textureLod(u_S, vec2(0), 0.0).r;\n" + + "#endif\n" + + "}\n"), + Arguments.of("terrainOpaqueLayout_keepsBlockTex", "uniform sampler2D u_BlockTex", + "#version 330 core\n" + + "#define USE_RGSS\n" + + "in vec2 v_TexCoord;\n" + + "out vec4 fragColor;\n" + + "uniform sampler2D u_BlockTex;\n" + + "#ifdef USE_RGSS\n" + + "vec4 sampleRGSS(sampler2D tex, vec2 uv) { return textureLod(tex, uv, 0.0); }\n" + + "#endif\n" + + "void main() {\n" + + "#ifdef USE_RGSS\n" + + " vec2 texSize = vec2(textureSize(u_BlockTex, 0));\n" + + " fragColor = sampleRGSS(u_BlockTex, v_TexCoord);\n" + + "#else\n" + + " fragColor = texture(u_BlockTex, v_TexCoord);\n" + + "#endif\n" + + "}\n"), + Arguments.of("samplerOnlyInMacroBody_isPreserved", "uniform sampler2D u_S", + "#version 460 core\n" + + "layout(binding = 0) uniform sampler2D u_S;\n" + + "#define SAMPLE_IT() textureLod(u_S, vec2(0), 0.0).r\n" + + "void main() {\n" + + " float a = SAMPLE_IT();\n" + + "}\n")); } - @Test - void samplerReferencedUnconditionally_isPreserved() { - final String src = "#version 460 core\n" - + "#define FOO 1\n" - + "layout(binding = 0) uniform sampler2D u_S;\n" - + "void main() {\n" - + " float t = textureLod(u_S, vec2(0), 0.0).r;\n" - + "#if FOO\n" - + " float a = t * 2.0;\n" - + "#endif\n" - + "}\n"; + @ParameterizedTest(name = "{0}") + @MethodSource("referenced") + void referencedSamplerIsPreserved(String name, String declaration, String src) { final String out = ShaderTransformChain.stripUnused(src); - assertTrue(out.contains("uniform sampler2D u_S"), - "unconditional ref must keep the decl; output:\n" + out); + assertTrue(out.contains(declaration), name + ": the declaration must survive; output:\n" + out); } - @Test - void samplerReferencedInBothIfAndElseBranches_isPreserved() { - final String src = "#version 460 core\n" - + "#define FOO 0\n" - + "layout(binding = 0) uniform sampler2D u_S;\n" - + "void main() {\n" - + "#if FOO\n" - + " float a = textureLod(u_S, vec2(0), 0.0).r;\n" - + "#else\n" - + " float b = textureLod(u_S, vec2(0), 0.0).r;\n" - + "#endif\n" - + "}\n"; - final String out = ShaderTransformChain.stripUnused(src); - assertTrue(out.contains("uniform sampler2D u_S"), "refs in both branches must keep the decl; output:\n" + out); + static Stream unreferenced() { + return Stream.of( + Arguments.of("samplerNeverReferenced_isStripped", "uniform sampler2D iris_centerDepthSmooth", + "#version 460 core\n" + + "#define FOO 1\n" + + "layout(binding = 0) uniform sampler2D iris_centerDepthSmooth;\n" + + "void main() {\n" + + "#if FOO\n" + + " float a = 1.0;\n" + + "#endif\n" + + "}\n"), + Arguments.of("samplerOnlyInCommentInsidePreprocessorBlock_isStripped", "uniform sampler2D u_S", + "#version 460 core\n" + + "#define FOO 1\n" + + "layout(binding = 0) uniform sampler2D u_S;\n" + + "void main() {\n" + + "#if FOO\n" + + " // u_S was used here once\n" + + " float a = 1.0;\n" + + "#endif\n" + + "}\n")); } - @Test - void terrainOpaqueLayout_keepsBlockTex() { - final String src = "#version 330 core\n" - + "#define USE_RGSS\n" - + "in vec2 v_TexCoord;\n" - + "out vec4 fragColor;\n" - + "uniform sampler2D u_BlockTex;\n" - + "#ifdef USE_RGSS\n" - + "vec4 sampleRGSS(sampler2D tex, vec2 uv) { return textureLod(tex, uv, 0.0); }\n" - + "#endif\n" - + "void main() {\n" - + "#ifdef USE_RGSS\n" - + " vec2 texSize = vec2(textureSize(u_BlockTex, 0));\n" - + " fragColor = sampleRGSS(u_BlockTex, v_TexCoord);\n" - + "#else\n" - + " fragColor = texture(u_BlockTex, v_TexCoord);\n" - + "#endif\n" - + "}\n"; + @ParameterizedTest(name = "{0}") + @MethodSource("unreferenced") + void unreferencedSamplerIsStripped(String name, String declaration, String src) { final String out = ShaderTransformChain.stripUnused(src); - assertTrue(out.contains("uniform sampler2D u_BlockTex"), "terrain sampler must survive; output:\n" + out); - } - - @Test - void samplerNeverReferenced_isStripped() { - final String src = "#version 460 core\n" - + "#define FOO 1\n" - + "layout(binding = 0) uniform sampler2D iris_centerDepthSmooth;\n" - + "void main() {\n" - + "#if FOO\n" - + " float a = 1.0;\n" - + "#endif\n" - + "}\n"; - final String out = ShaderTransformChain.stripUnused(src); - assertFalse(out.contains("uniform sampler2D iris_centerDepthSmooth"), "an unreferenced sampler must still be stripped; output:\n" + out); - } - - @Test - void samplerOnlyInCommentInsidePreprocessorBlock_isStripped() { - final String src = "#version 460 core\n" - + "#define FOO 1\n" - + "layout(binding = 0) uniform sampler2D u_S;\n" - + "void main() {\n" - + "#if FOO\n" - + " // u_S was used here once\n" - + " float a = 1.0;\n" - + "#endif\n" - + "}\n"; - final String out = ShaderTransformChain.stripUnused(src); - assertFalse(out.contains("uniform sampler2D u_S"), "a comment mention must not keep the decl; output:\n" + out); + assertFalse(out.contains(declaration), name + ": the declaration must be stripped; output:\n" + out); } @Test @@ -114,16 +117,4 @@ class SdlSamplerStripperPreprocessorTest { + "}\n"; assertSame(src, ShaderTransformChain.stripUnused(src)); } - - @Test - void samplerOnlyInMacroBody_isPreserved() { - final String src = "#version 460 core\n" - + "layout(binding = 0) uniform sampler2D u_S;\n" - + "#define SAMPLE_IT() textureLod(u_S, vec2(0), 0.0).r\n" - + "void main() {\n" - + " float a = SAMPLE_IT();\n" - + "}\n"; - final String out = ShaderTransformChain.stripUnused(src); - assertTrue(out.contains("uniform sampler2D u_S"), "a macro-body ref must keep the decl; output:\n" + out); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ShaderCompilationTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ShaderCompilationTest.java deleted file mode 100644 index 9ce20e9fb..000000000 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ShaderCompilationTest.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.gtnewhorizons.angelica.sdlgpu.shader; - -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; -import org.junit.jupiter.api.Test; -import org.lwjgl.util.shaderc.Shaderc; - -import java.nio.ByteBuffer; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Tests GLSL -> SPIR-V compilation via shaderc. - */ -class ShaderCompilationTest { - - private static final String SIMPLE_VERTEX_SHADER = """ - #version 460 core - layout(location = 0) in vec3 a_Position; - layout(location = 1) in vec4 a_Color; - out vec4 v_Color; - void main() { - gl_Position = vec4(a_Position, 1.0); - v_Color = a_Color; - } - """; - - private static final String SIMPLE_FRAGMENT_SHADER = """ - #version 460 core - in vec4 v_Color; - out vec4 fragColor; - void main() { - fragColor = v_Color; - } - """; - - private static final String TEXTURED_FRAGMENT_SHADER = """ - #version 460 core - in vec2 v_TexCoord; - out vec4 fragColor; - uniform sampler2D u_Texture; - void main() { - fragColor = texture(u_Texture, v_TexCoord); - } - """; - - private static final String UNIFORM_VERTEX_SHADER = """ - #version 460 core - layout(location = 0) in vec3 a_Position; - uniform mat4 u_ModelViewProjection; - uniform vec4 u_Color; - out vec4 v_Color; - void main() { - gl_Position = u_ModelViewProjection * vec4(a_Position, 1.0); - v_Color = u_Color; - } - """; - - @Test - void testSimpleVertexShaderCompiles() { - ByteBuffer spirv = compileShader(SIMPLE_VERTEX_SHADER, Shaderc.shaderc_vertex_shader); - assertNotNull(spirv, "Vertex shader should compile to SPIR-V"); - assertTrue(spirv.remaining() > 0, "SPIR-V output should not be empty"); - assertEquals(0x07230203, spirv.getInt(0), "Output should start with SPIR-V magic number"); - } - - @Test - void testSimpleFragmentShaderCompiles() { - ByteBuffer spirv = compileShader(SIMPLE_FRAGMENT_SHADER, Shaderc.shaderc_fragment_shader); - assertNotNull(spirv, "Fragment shader should compile to SPIR-V"); - assertTrue(spirv.remaining() > 0, "SPIR-V output should not be empty"); - assertEquals(0x07230203, spirv.getInt(0), "Output should start with SPIR-V magic number"); - } - - @Test - void testTexturedFragmentShaderCompiles() { - ByteBuffer spirv = compileShader(TEXTURED_FRAGMENT_SHADER, Shaderc.shaderc_fragment_shader); - assertNotNull(spirv, "Textured fragment shader should compile to SPIR-V"); - assertEquals(0x07230203, spirv.getInt(0)); - } - - @Test - void testUniformVertexShaderCompiles() { - ByteBuffer spirv = compileShader(UNIFORM_VERTEX_SHADER, Shaderc.shaderc_vertex_shader); - assertNotNull(spirv, "Uniform vertex shader should compile to SPIR-V"); - assertEquals(0x07230203, spirv.getInt(0)); - } - - @Test - void testInvalidShaderFailsGracefully() { - long compiler = Shaderc.shaderc_compiler_initialize(); - long options = Shaderc.shaderc_compile_options_initialize(); - - try { - Shaderc.shaderc_compile_options_set_forced_version_profile(options, 460, Shaderc.shaderc_profile_core); - - long result = Shaderc.shaderc_compile_into_spv( - compiler, "this is not valid GLSL", Shaderc.shaderc_vertex_shader, "bad_shader", "main", options); - - try { - final int status = Shaderc.shaderc_result_get_compilation_status(result); - assertNotEquals(Shaderc.shaderc_compilation_status_success, status, "Invalid GLSL should not compile successfully"); - - final String error = Shaderc.shaderc_result_get_error_message(result); - assertNotNull(error, "Error message should be present"); - assertFalse(error.isEmpty(), "Error message should not be empty"); - } finally { - Shaderc.shaderc_result_release(result); - } - } finally { - Shaderc.shaderc_compile_options_release(options); - Shaderc.shaderc_compiler_release(compiler); - } - } - - @Test - void testAutoBindUniformsProducesBindings() { - // Verify that shaderc's auto_bind_uniforms mode assigns bindings - final ByteBuffer spirv = compileShader(UNIFORM_VERTEX_SHADER, Shaderc.shaderc_vertex_shader); - assertNotNull(spirv, "Should compile with auto-bind-uniforms"); - assertTrue(spirv.remaining() > 100, "SPIR-V should be substantial enough to contain bindings"); - } - - private ByteBuffer compileShader(String source, int shaderKind) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, shaderKind, "test_shader", SpirvCompiler.Options.vulkanForced460Core()); - if (r.spirv() == null) { - fail("Shader compilation failed: " + r.error()); - } - return r.spirv(); - } -} diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ShaderStageSerializerTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ShaderStageSerializerTest.java index c9aa76b73..ec8dad841 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ShaderStageSerializerTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/ShaderStageSerializerTest.java @@ -3,7 +3,6 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; import com.gtnewhorizons.angelica.glsm.hooks.PerFrameUniformBlock; import com.gtnewhorizons.angelica.glsm.hooks.PerFrameUniformBlock.Member; import com.gtnewhorizons.angelica.glsm.shader.ShaderCacheIO; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import com.gtnewhorizons.angelica.glsm.shader.UniformType; import org.junit.jupiter.api.Test; import org.lwjgl.opengl.GL20; @@ -15,6 +14,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -65,9 +65,7 @@ class ShaderStageSerializerTest { private record Staged(byte[] spirv, ShaderManager.StageReflection reflection, ShaderManager.GraphicsBindingMap map) {} private static Staged stage(String source, int shaderKind, int glType) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, shaderKind, "codec", SpirvCompiler.Options.vulkanForced460Core()); - assertNotNull(r.spirv(), () -> "shaderc failed: " + r.error()); - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(source, shaderKind); try { final ShaderManager.GraphicsBindingMap map = ShaderManager.remapSpirvForSDLGPU(spirv, glType); final ShaderManager.StageReflection reflection = ShaderManager.reflectStage(spirv, glType == GL20.GL_VERTEX_SHADER); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SpirvTestShaders.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SpirvTestShaders.java index dd4467a76..18f555d58 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SpirvTestShaders.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SpirvTestShaders.java @@ -1,9 +1,26 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; +import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; + +import java.nio.ByteBuffer; + +import static org.junit.jupiter.api.Assertions.fail; + public final class SpirvTestShaders { private SpirvTestShaders() {} + /** Caller frees the result. */ + public static ByteBuffer compileOrFail(String source, int shaderKind) { + return compileOrFail(source, shaderKind, SpirvCompiler.Options.vulkanForced460Core()); + } + + public static ByteBuffer compileOrFail(String source, int shaderKind, SpirvCompiler.Options options) { + final SpirvCompiler.Result r = SpirvCompiler.compile(source, shaderKind, "test_shader", options); + if (r.spirv() == null) fail("shader compilation failed: " + r.error() + "\nsource:\n" + source); + return r.spirv(); + } + public static final String VERTEX_GLSL = """ #version 460 core layout(set = 0, binding = 0) uniform sampler2D u_Texture; diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SpvcArrayStrideTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SpvcArrayStrideTest.java index d391d09dc..8029eefcd 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SpvcArrayStrideTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SpvcArrayStrideTest.java @@ -1,15 +1,18 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.system.MemoryUtil; import org.lwjgl.util.shaderc.Shaderc; import java.nio.ByteBuffer; +import java.util.stream.Stream; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.fail; class SpvcArrayStrideTest { @@ -33,89 +36,6 @@ class SpvcArrayStrideTest { } """; - @Test - void floatArrayHasStride16() { - final ByteBuffer spirv = compile(FRAGMENT_WITH_FLOAT_ARRAY, Shaderc.shaderc_fragment_shader); - try { - final ShaderManager.StageReflection refl = ShaderManager.reflectStage(spirv, false); - final ShaderManager.UboMember arr = findMember(refl, "arr"); - assertNotNull(arr, "arr member missing from reflection: " + refl.uboMembers()); - assertEquals(16, arr.arrayStride(), "float[N] array stride should be 16 bytes under std140"); - assertEquals(256, arr.size(), "float[16] declared size should be 256 bytes under std140"); - } finally { - MemoryUtil.memFree(spirv); - } - } - - @Test - void vec4ArrayHasStride16() { - final ByteBuffer spirv = compile(FRAGMENT_WITH_VEC4_ARRAY, Shaderc.shaderc_fragment_shader); - try { - final ShaderManager.StageReflection refl = ShaderManager.reflectStage(spirv, false); - final ShaderManager.UboMember arr = findMember(refl, "colors"); - assertNotNull(arr, "colors member missing from reflection: " + refl.uboMembers()); - assertEquals(16, arr.arrayStride(), "vec4[N] array stride should be 16 bytes under std140"); - assertEquals(128, arr.size(), "vec4[8] declared size should be 128 bytes under std140"); - } finally { - MemoryUtil.memFree(spirv); - } - } - - @Test - void scalarMemberHasZeroStride() { - final ByteBuffer spirv = compile(FRAGMENT_WITH_FLOAT_ARRAY, Shaderc.shaderc_fragment_shader); - try { - final ShaderManager.StageReflection refl = ShaderManager.reflectStage(spirv, false); - final ShaderManager.UboMember idx = findMember(refl, "idx"); - assertNotNull(idx, "idx member missing from reflection: " + refl.uboMembers()); - assertEquals(0, idx.arrayStride(), "non-array members must have arrayStride=0"); - } finally { - MemoryUtil.memFree(spirv); - } - } - - @Test - void typeDescriptor_floatScalar() { - final ByteBuffer spirv = compile(FRAGMENT_WITH_FLOAT_ARRAY, Shaderc.shaderc_fragment_shader); - try { - final ShaderManager.UboMember idx = findMember(ShaderManager.reflectStage(spirv, false), "idx"); - assertNotNull(idx); - assertEquals(1, idx.vectorSize(), "scalar vectorSize"); - assertEquals(1, idx.columns(), "scalar columns"); - assertEquals(1, idx.arrayLen(), "non-array arrayLen=1"); - } finally { - MemoryUtil.memFree(spirv); - } - } - - @Test - void typeDescriptor_floatArray() { - final ByteBuffer spirv = compile(FRAGMENT_WITH_FLOAT_ARRAY, Shaderc.shaderc_fragment_shader); - try { - final ShaderManager.UboMember arr = findMember(ShaderManager.reflectStage(spirv, false), "arr"); - assertNotNull(arr); - assertEquals(1, arr.vectorSize(), "scalar array vectorSize"); - assertEquals(1, arr.columns(), "scalar array columns"); - assertEquals(16, arr.arrayLen(), "float[16] arrayLen"); - } finally { - MemoryUtil.memFree(spirv); - } - } - - @Test - void typeDescriptor_vec4Array() { - final ByteBuffer spirv = compile(FRAGMENT_WITH_VEC4_ARRAY, Shaderc.shaderc_fragment_shader); - try { - final ShaderManager.UboMember colors = findMember(ShaderManager.reflectStage(spirv, false), "colors"); - assertNotNull(colors); - assertEquals(4, colors.vectorSize(), "vec4 vectorSize"); - assertEquals(1, colors.columns(), "vec4 columns"); - assertEquals(8, colors.arrayLen(), "vec4[8] arrayLen"); - } finally { - MemoryUtil.memFree(spirv); - } - } - private static final String FRAGMENT_WITH_VEC3 = """ #version 330 core uniform vec3 dir; @@ -123,21 +43,6 @@ class SpvcArrayStrideTest { void main() { fragColor = vec4(dir, 1.0); } """; - @Test - void typeDescriptor_vec3() { - final ByteBuffer spirv = compile(FRAGMENT_WITH_VEC3, Shaderc.shaderc_fragment_shader); - try { - final ShaderManager.UboMember dir = findMember(ShaderManager.reflectStage(spirv, false), "dir"); - assertNotNull(dir); - assertEquals(3, dir.vectorSize(), "vec3 vectorSize"); - assertEquals(1, dir.columns(), "vec3 columns"); - assertEquals(1, dir.arrayLen(), "non-array"); - assertEquals(0, dir.arrayStride(), "non-array stride=0"); - } finally { - MemoryUtil.memFree(spirv); - } - } - private static final String FRAGMENT_WITH_MAT3 = """ #version 330 core uniform mat3 normalMatrix; @@ -145,16 +50,34 @@ class SpvcArrayStrideTest { void main() { fragColor = vec4(normalMatrix * vec3(0,1,0), 1.0); } """; - @Test - void typeDescriptor_mat3() { - final ByteBuffer spirv = compile(FRAGMENT_WITH_MAT3, Shaderc.shaderc_fragment_shader); - try { - final ShaderManager.UboMember m = findMember(ShaderManager.reflectStage(spirv, false), "normalMatrix"); - assertNotNull(m); - assertEquals(3, m.vectorSize(), "mat3 rows"); - assertEquals(3, m.columns(), "mat3 columns"); - assertEquals(1, m.arrayLen(), "non-array"); + private static final int UNCHECKED = -1; + private static Arguments member(String name, String source, String member, int vectorSize, int columns, int arrayLen, int arrayStride, int size) { + return Arguments.of(name, source, member, vectorSize, columns, arrayLen, arrayStride, size); + } + + static Stream members() { + return Stream.of( + member("int scalar", FRAGMENT_WITH_FLOAT_ARRAY, "idx", 1, 1, 1, 0, UNCHECKED), + member("float[16]", FRAGMENT_WITH_FLOAT_ARRAY, "arr", 1, 1, 16, 16, 256), + member("vec4[8]", FRAGMENT_WITH_VEC4_ARRAY, "colors", 4, 1, 8, 16, 128), + member("vec3", FRAGMENT_WITH_VEC3, "dir", 3, 1, 1, 0, UNCHECKED), + member("mat3", FRAGMENT_WITH_MAT3, "normalMatrix", 3, 3, 1, UNCHECKED, UNCHECKED)); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("members") + void reflectsStd140MemberLayout(String name, String source, String member, int vectorSize, int columns, int arrayLen, int arrayStride, int size) { + final ByteBuffer spirv = compileOrFail(source, Shaderc.shaderc_fragment_shader, SpirvCompiler.Options.vulkanRelaxed()); + try { + final ShaderManager.StageReflection refl = ShaderManager.reflectStage(spirv, false); + final ShaderManager.UboMember m = findMember(refl, member); + assertNotNull(m, member + " missing from reflection: " + refl.uboMembers()); + assertEquals(vectorSize, m.vectorSize(), name + " vectorSize"); + assertEquals(columns, m.columns(), name + " columns"); + assertEquals(arrayLen, m.arrayLen(), name + " arrayLen (1 for non-arrays)"); + if (arrayStride != UNCHECKED) assertEquals(arrayStride, m.arrayStride(), name + " std140 arrayStride (0 for non-arrays)"); + if (size != UNCHECKED) assertEquals(size, m.size(), name + " std140 declared size"); } finally { MemoryUtil.memFree(spirv); } @@ -166,12 +89,4 @@ class SpvcArrayStrideTest { } return null; } - - private static ByteBuffer compile(String source, int shaderKind) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, shaderKind, "test", SpirvCompiler.Options.vulkanRelaxed()); - if (r.spirv() == null) { - fail("Compilation failed: " + r.error()); - } - return r.spirv(); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SpvcReflectionTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SpvcReflectionTest.java deleted file mode 100644 index 9705312cc..000000000 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/SpvcReflectionTest.java +++ /dev/null @@ -1,181 +0,0 @@ -package com.gtnewhorizons.angelica.sdlgpu.shader; - -import org.lwjgl.system.MemoryStack; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; -import org.junit.jupiter.api.Test; -import org.lwjgl.PointerBuffer; -import org.lwjgl.util.shaderc.Shaderc; -import org.lwjgl.util.spvc.Spvc; -import org.lwjgl.util.spvc.SpvcReflectedResource; - -import java.nio.ByteBuffer; -import java.nio.IntBuffer; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; -import static org.lwjgl.system.MemoryStack.*; - -/** - * Tests SPIRV-Cross reflection on compiled SPIR-V: Verifies we can extract uniform buffer names, sampler names, and vertex attribute locations. - */ -class SpvcReflectionTest { - - private static final String VERTEX_WITH_UNIFORMS = """ - #version 460 core - layout(location = 0) in vec3 a_Position; - layout(location = 1) in vec2 a_TexCoord; - uniform mat4 u_ModelViewProjection; - uniform vec4 u_Color; - out vec2 v_TexCoord; - out vec4 v_Color; - void main() { - gl_Position = u_ModelViewProjection * vec4(a_Position, 1.0); - v_TexCoord = a_TexCoord; - v_Color = u_Color; - } - """; - - private static final String FRAGMENT_WITH_SAMPLER = """ - #version 460 core - in vec2 v_TexCoord; - in vec4 v_Color; - out vec4 fragColor; - uniform sampler2D u_Texture; - uniform float u_Alpha; - void main() { - vec4 texColor = texture(u_Texture, v_TexCoord); - fragColor = texColor * v_Color * vec4(1.0, 1.0, 1.0, u_Alpha); - } - """; - - @Test - void testReflectUniformBuffersFromVertexShader() { - ByteBuffer spirv = compileWithAutoBindUniforms(VERTEX_WITH_UNIFORMS, Shaderc.shaderc_vertex_shader); - assertNotNull(spirv); - - List uniformBuffers = reflectResourceNames(spirv, Spvc.SPVC_RESOURCE_TYPE_UNIFORM_BUFFER); - assertFalse(uniformBuffers.isEmpty(), "Should find uniform buffers after auto-bind compilation"); - } - - @Test - void testReflectSampledImagesFromFragmentShader() { - ByteBuffer spirv = compileWithAutoBindUniforms(FRAGMENT_WITH_SAMPLER, Shaderc.shaderc_fragment_shader); - assertNotNull(spirv); - - List sampledImages = reflectResourceNames(spirv, Spvc.SPVC_RESOURCE_TYPE_SAMPLED_IMAGE); - // With auto-combined-image-sampler, separate sampler+image become combined - // Check both combined and separate paths - List separateImages = reflectResourceNames(spirv, Spvc.SPVC_RESOURCE_TYPE_SEPARATE_IMAGE); - List separateSamplers = reflectResourceNames(spirv, Spvc.SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS); - - boolean foundTexture = sampledImages.stream().anyMatch(n -> n.contains("u_Texture")) - || separateImages.stream().anyMatch(n -> n.contains("u_Texture")) - || separateSamplers.stream().anyMatch(n -> n.contains("u_Texture")); - - assertTrue(foundTexture, "Should find u_Texture in sampled images, separate images, or separate samplers. " - + "Combined: " + sampledImages + ", Images: " + separateImages + ", Samplers: " + separateSamplers); - } - - @Test - void testReflectProducesNonEmptyNames() { - ByteBuffer spirv = compileWithAutoBindUniforms(VERTEX_WITH_UNIFORMS, Shaderc.shaderc_vertex_shader); - assertNotNull(spirv); - - try (var stack = stackPush()) { - long ctx = createSpvcContext(stack); - long compiler = createGLSLCompiler(ctx, spirv, stack); - long resources = createResources(compiler, stack); - - // Get uniform buffers - PointerBuffer pList = stack.pointers(0); - PointerBuffer pCount = stack.pointers(0); - assertEquals(Spvc.SPVC_SUCCESS, - Spvc.spvc_resources_get_resource_list_for_type(resources, Spvc.SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, pList, pCount)); - - long count = pCount.get(0); - if (count > 0) { - SpvcReflectedResource.Buffer resList = SpvcReflectedResource.create(pList.get(0), (int) count); - for (int i = 0; i < count; i++) { - String name = resList.get(i).nameString(); - assertNotNull(name, "Resource name should not be null"); - assertFalse(name.isEmpty(), "Resource name should not be empty"); - } - } - - Spvc.spvc_context_destroy(ctx); - } - } - - @Test - void testSpvcContextCreationAndDestruction() { - try (var stack = stackPush()) { - PointerBuffer pCtx = stack.pointers(0); - assertEquals(Spvc.SPVC_SUCCESS, Spvc.spvc_context_create(pCtx)); - long ctx = pCtx.get(0); - assertNotEquals(0, ctx, "Context should be non-null"); - Spvc.spvc_context_destroy(ctx); - } - } - - private List reflectResourceNames(ByteBuffer spirv, int resourceType) { - List names = new ArrayList<>(); - try (var stack = stackPush()) { - long ctx = createSpvcContext(stack); - long compiler = createGLSLCompiler(ctx, spirv, stack); - long resources = createResources(compiler, stack); - - PointerBuffer pList = stack.pointers(0); - PointerBuffer pCount = stack.pointers(0); - if (Spvc.spvc_resources_get_resource_list_for_type(resources, resourceType, pList, pCount) == Spvc.SPVC_SUCCESS) { - long count = pCount.get(0); - if (count > 0) { - SpvcReflectedResource.Buffer resList = SpvcReflectedResource.create(pList.get(0), (int) count); - for (int i = 0; i < count; i++) { - String name = resList.get(i).nameString(); - if (name != null && !name.isEmpty()) { - names.add(name); - } - } - } - } - - Spvc.spvc_context_destroy(ctx); - } - return names; - } - - private long createSpvcContext(MemoryStack stack) { - PointerBuffer pCtx = stack.pointers(0); - assertEquals(Spvc.SPVC_SUCCESS, Spvc.spvc_context_create(pCtx)); - return pCtx.get(0); - } - - private long createGLSLCompiler(long ctx, ByteBuffer spirv, MemoryStack stack) { - IntBuffer spirvWords = spirv.asIntBuffer(); - PointerBuffer pIR = stack.pointers(0); - assertEquals(Spvc.SPVC_SUCCESS, - Spvc.spvc_context_parse_spirv(ctx, spirvWords, spirvWords.remaining(), pIR)); - - PointerBuffer pCompiler = stack.pointers(0); - assertEquals(Spvc.SPVC_SUCCESS, - Spvc.spvc_context_create_compiler(ctx, Spvc.SPVC_BACKEND_GLSL, pIR.get(0), - Spvc.SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, pCompiler)); - return pCompiler.get(0); - } - - private long createResources(long compiler, MemoryStack stack) { - PointerBuffer pResources = stack.pointers(0); - assertEquals(Spvc.SPVC_SUCCESS, - Spvc.spvc_compiler_create_shader_resources(compiler, pResources)); - return pResources.get(0); - } - - private ByteBuffer compileWithAutoBindUniforms(String source, int shaderKind) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, shaderKind, "test", SpirvCompiler.Options.vulkanForced460Core()); - if (r.spirv() == null) { - fail("Compilation failed: " + r.error()); - } - return r.spirv(); - } -} diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/Std140WriterTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/Std140WriterTest.java index b766ebb3d..9ae11a814 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/Std140WriterTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/Std140WriterTest.java @@ -1,10 +1,14 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.system.MemoryUtil; import org.lwjgl.util.spvc.Spvc; import java.nio.FloatBuffer; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -32,14 +36,26 @@ class Std140WriterTest { assertEquals(expected, actual, 1e-6f); } - @Test - void scalar_writesOneFloatAtOffset() { - final FloatBuffer ubo = outBuf(8); + private static Arguments nonArray(String name, int byteOffset, int vectorSize, float[] values, float... expectedUbo) { + return Arguments.of(name, byteOffset, vectorSize, values, expectedUbo); + } + + /** Expected is the whole 8-float UBO, so slots the write must not touch are checked too. */ + static Stream nonArrays() { + return Stream.of( + nonArray("scalar at byte offset 4", 4, 1, new float[]{42f}, 0, 42, 0, 0, 0, 0, 0, 0), + nonArray("vec2", 0, 2, new float[]{1f, 2f}, 1, 2, 0, 0, 0, 0, 0, 0), + nonArray("vec3 leaves the fourth lane alone", 0, 3, new float[]{1f, 2f, 3f}, 1, 2, 3, 0, 0, 0, 0, 0), + nonArray("vec4", 0, 4, new float[]{1f, 2f, 3f, 4f}, 1, 2, 3, 4, 0, 0, 0, 0)); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("nonArrays") + void nonArray_writesPackedComponentsAtOffset(String name, int byteOffset, int vectorSize, float[] values, float[] expectedUbo) { + final FloatBuffer ubo = outBuf(expectedUbo.length); try { - Std140Writer.write(ubo, single(/*off*/ 4, /*size*/ 4, 1, 1), new float[]{42f}, 1); - assertEq(0f, ubo.get(0), "[0] before offset"); - assertEq(42f, ubo.get(1), "[1] = scalar"); - assertEq(0f, ubo.get(2), "[2] untouched"); + Std140Writer.write(ubo, single(byteOffset, vectorSize * 4, vectorSize, 1), values, 1); + for (int i = 0; i < expectedUbo.length; i++) assertEq(expectedUbo[i], ubo.get(i), "[" + i + "]"); } finally { MemoryUtil.memFree(ubo); } @@ -60,47 +76,6 @@ class Std140WriterTest { } } - @Test - void vec2_writesTwoFloatsPacked() { - final FloatBuffer ubo = outBuf(8); - try { - Std140Writer.write(ubo, single(/*off*/ 0, /*size*/ 8, 2, 1), new float[]{1f, 2f}, 1); - assertEq(1f, ubo.get(0), "x"); - assertEq(2f, ubo.get(1), "y"); - assertEq(0f, ubo.get(2), "after vec2"); - } finally { - MemoryUtil.memFree(ubo); - } - } - - @Test - void vec3_writesThreeFloatsPacked_noTrailingPaddingTouched() { - final FloatBuffer ubo = outBuf(8); - try { - Std140Writer.write(ubo, single(/*off*/ 0, /*size*/ 12, 3, 1), new float[]{1f, 2f, 3f}, 1); - assertEq(1f, ubo.get(0), "x"); - assertEq(2f, ubo.get(1), "y"); - assertEq(3f, ubo.get(2), "z"); - assertEq(0f, ubo.get(3), "no spurious write past vec3"); - } finally { - MemoryUtil.memFree(ubo); - } - } - - @Test - void vec4_writesFourFloatsPacked() { - final FloatBuffer ubo = outBuf(8); - try { - Std140Writer.write(ubo, single(/*off*/ 0, /*size*/ 16, 4, 1), new float[]{1f, 2f, 3f, 4f}, 1); - assertEq(1f, ubo.get(0), "x"); - assertEq(2f, ubo.get(1), "y"); - assertEq(3f, ubo.get(2), "z"); - assertEq(4f, ubo.get(3), "w"); - } finally { - MemoryUtil.memFree(ubo); - } - } - @Test void vec3Array_eachElementAtStride16_zeroPaddedFourthLane() { diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/UniformBoolRemapTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/UniformBoolRemapTest.java index b8a43cfe5..7c79c66f5 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/UniformBoolRemapTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/UniformBoolRemapTest.java @@ -1,34 +1,32 @@ package com.gtnewhorizons.angelica.sdlgpu.shader; - import com.gtnewhorizons.angelica.sdlgpu.resource.FormatMap; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL30; +import java.util.stream.Stream; + import static org.junit.jupiter.api.Assertions.assertEquals; class UniformBoolRemapTest { - @Test - void scalarUintToBool() { - assertEquals(GL20.GL_BOOL, FormatMap.remapUnsignedIntToBool(GL11.GL_UNSIGNED_INT)); + static Stream unsignedTypes() { + return Stream.of( + Arguments.of("uint -> bool", GL11.GL_UNSIGNED_INT, GL20.GL_BOOL), + Arguments.of("uvec2 -> bvec2", GL30.GL_UNSIGNED_INT_VEC2, GL20.GL_BOOL_VEC2), + Arguments.of("uvec3 -> bvec3", GL30.GL_UNSIGNED_INT_VEC3, GL20.GL_BOOL_VEC3), + Arguments.of("uvec4 -> bvec4", GL30.GL_UNSIGNED_INT_VEC4, GL20.GL_BOOL_VEC4)); } - @Test - void uvec2ToBvec2() { - assertEquals(GL20.GL_BOOL_VEC2, FormatMap.remapUnsignedIntToBool(GL30.GL_UNSIGNED_INT_VEC2)); - } - - @Test - void uvec3ToBvec3() { - assertEquals(GL20.GL_BOOL_VEC3, FormatMap.remapUnsignedIntToBool(GL30.GL_UNSIGNED_INT_VEC3)); - } - - @Test - void uvec4ToBvec4() { - assertEquals(GL20.GL_BOOL_VEC4, FormatMap.remapUnsignedIntToBool(GL30.GL_UNSIGNED_INT_VEC4)); + @ParameterizedTest(name = "{0}") + @MethodSource("unsignedTypes") + void unsignedIntTypeRemapsToBool(String name, int unsignedType, int boolType) { + assertEquals(boolType, FormatMap.remapUnsignedIntToBool(unsignedType)); } @Test diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/UnusedSamplerReflectionTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/UnusedSamplerReflectionTest.java index cb011ea74..047f234af 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/UnusedSamplerReflectionTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/UnusedSamplerReflectionTest.java @@ -8,8 +8,8 @@ import org.lwjgl.util.shaderc.Shaderc; import java.nio.ByteBuffer; import java.util.List; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; class UnusedSamplerReflectionTest { @@ -31,9 +31,7 @@ class UnusedSamplerReflectionTest { @Test void samplersOnlyShadowedOrDeclaredAreUnused() { - final SpirvCompiler.Result r = SpirvCompiler.compile(FRAGMENT, Shaderc.shaderc_fragment_shader, "test", SpirvCompiler.Options.vulkanRelaxed()); - if (r.spirv() == null) fail("Compilation failed: " + r.error()); - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(FRAGMENT, Shaderc.shaderc_fragment_shader, SpirvCompiler.Options.vulkanRelaxed()); try { final ShaderManager.StageReflection refl = ShaderManager.reflectStage(spirv, false); assertEquals(List.of("normals", "specular"), refl.unusedSamplerNames().stream().sorted().toList(), diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/cross/LocalSizeParseTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/cross/LocalSizeParseTest.java index d0b3d7f43..2bda5a03d 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/cross/LocalSizeParseTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/cross/LocalSizeParseTest.java @@ -1,15 +1,18 @@ package com.gtnewhorizons.angelica.sdlgpu.shader.cross; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.system.MemoryUtil; import org.lwjgl.util.shaderc.Shaderc; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.stream.Stream; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.fail; class LocalSizeParseTest { @@ -34,31 +37,19 @@ class LocalSizeParseTest { void main() { buf.b[gl_GlobalInvocationID.x] = 1u; } """; - @Test - void parsesLocalSize8x8x1() { - final ByteBuffer spirv = compile(LS_8_8_1); - try { - assertArrayEquals(new int[]{8, 8, 1}, CrossCompileUtil.parseLocalSize(spirv)); - } finally { - MemoryUtil.memFree(spirv); - } + static Stream localSizes() { + return Stream.of( + Arguments.of("8x8x1", LS_8_8_1, new int[]{8, 8, 1}), + Arguments.of("64x1x1", LS_64, new int[]{64, 1, 1}), + Arguments.of("4x4x4", LS_4_4_4, new int[]{4, 4, 4})); } - @Test - void parsesLocalSize64x1x1() { - final ByteBuffer spirv = compile(LS_64); + @ParameterizedTest(name = "{0}") + @MethodSource("localSizes") + void parsesLocalSize(String name, String source, int[] expected) { + final ByteBuffer spirv = compileOrFail(source, Shaderc.shaderc_compute_shader); try { - assertArrayEquals(new int[]{64, 1, 1}, CrossCompileUtil.parseLocalSize(spirv)); - } finally { - MemoryUtil.memFree(spirv); - } - } - - @Test - void parsesLocalSize4x4x4() { - final ByteBuffer spirv = compile(LS_4_4_4); - try { - assertArrayEquals(new int[]{4, 4, 4}, CrossCompileUtil.parseLocalSize(spirv)); + assertArrayEquals(expected, CrossCompileUtil.parseLocalSize(spirv)); } finally { MemoryUtil.memFree(spirv); } @@ -74,13 +65,4 @@ class LocalSizeParseTest { MemoryUtil.memFree(spirv); } } - - private static ByteBuffer compile(String source) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, Shaderc.shaderc_compute_shader, "ls_test", - SpirvCompiler.Options.vulkanForced460Core()); - if (r.spirv() == null) { - fail("Shader compilation failed: " + r.error()); - } - return r.spirv(); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/dxbc/DxbcCompileTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/dxbc/DxbcCompileTest.java index 6e2309a0f..c9ef3ea5a 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/dxbc/DxbcCompileTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/dxbc/DxbcCompileTest.java @@ -2,7 +2,6 @@ package com.gtnewhorizons.angelica.sdlgpu.shader.dxbc; import com.gtnewhorizons.angelica.glsm.shader.ShaderCacheIO; import com.gtnewhorizons.angelica.glsm.shader.ShaderDiskCache; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager; import com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders; @@ -12,6 +11,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledOnOs; import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL43; import org.lwjgl.system.MemoryUtil; @@ -21,12 +23,13 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.Map; +import java.util.stream.Stream; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; @EnabledOnOs(OS.WINDOWS) class DxbcCompileTest { @@ -43,56 +46,27 @@ class DxbcCompileTest { private static final byte DXBC_MAGIC_B = 'B'; private static final byte DXBC_MAGIC_C = 'C'; - - - @Test - void vertexShaderCrossCompilesAndProducesDxbc() { - final ByteBuffer spirv = compile(SpirvTestShaders.VERTEX_GLSL, Shaderc.shaderc_vertex_shader); - try { - ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_VERTEX_SHADER); - final CrossCompileCache.Output out = DxbcCrossCompile.compile(spirv, GL20.GL_VERTEX_SHADER); - try { - assertNotNull(out.code()); - assertTrue(out.code().remaining() >= 4, "DXBC output too short"); - assertDxbcMagic(out.code()); - assertEquals("main", out.entrypoint()); - } finally { - MemoryUtil.memFree(out.code()); - } - } finally { - MemoryUtil.memFree(spirv); - } + static Stream stages() { + return Stream.of( + Arguments.of("vertex", SpirvTestShaders.VERTEX_GLSL, Shaderc.shaderc_vertex_shader, GL20.GL_VERTEX_SHADER), + Arguments.of("fragment", SpirvTestShaders.FRAGMENT_GLSL, Shaderc.shaderc_fragment_shader, GL20.GL_FRAGMENT_SHADER), + Arguments.of("compute", SpirvTestShaders.HIZ_INIT_GLSL, Shaderc.shaderc_compute_shader, GL43.GL_COMPUTE_SHADER)); } - @Test - void fragmentShaderCrossCompilesAndProducesDxbc() { - final ByteBuffer spirv = compile(SpirvTestShaders.FRAGMENT_GLSL, Shaderc.shaderc_fragment_shader); + @ParameterizedTest(name = "{0}") + @MethodSource("stages") + void shaderCrossCompilesAndProducesDxbc(String name, String glsl, int shadercKind, int glStage) { + final ByteBuffer spirv = compileOrFail(glsl, shadercKind); try { - ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_FRAGMENT_SHADER); - final CrossCompileCache.Output out = DxbcCrossCompile.compile(spirv, GL20.GL_FRAGMENT_SHADER); - try { - assertNotNull(out.code()); - assertTrue(out.code().remaining() >= 4, "DXBC output too short"); - assertDxbcMagic(out.code()); - assertEquals("main", out.entrypoint()); - } finally { - MemoryUtil.memFree(out.code()); + if (glStage == GL43.GL_COMPUTE_SHADER) { + ShaderManager.remapSpirvForComputeSDLGPU(spirv); + } else { + ShaderManager.remapSpirvForSDLGPU(spirv, glStage); } - } finally { - MemoryUtil.memFree(spirv); - } - } - - - @Test - void computeShaderCrossCompilesAndProducesDxbc() { - final ByteBuffer spirv = compile(SpirvTestShaders.HIZ_INIT_GLSL, Shaderc.shaderc_compute_shader); - try { - ShaderManager.remapSpirvForComputeSDLGPU(spirv); - final CrossCompileCache.Output out = DxbcCrossCompile.compile(spirv, GL43.GL_COMPUTE_SHADER); + final CrossCompileCache.Output out = DxbcCrossCompile.compile(spirv, glStage); try { assertNotNull(out.code()); - assertTrue(out.code().remaining() >= 4, "DXBC compute output too short"); + assertTrue(out.code().remaining() >= 4, "DXBC " + name + " output too short"); assertDxbcMagic(out.code()); assertEquals("main", out.entrypoint()); } finally { @@ -115,25 +89,11 @@ class DxbcCompileTest { assertTrue(ex.getMessage().length() > "D3DCompile failed (HRESULT=0x80004005): ".length(), "Exception message should include error blob text: " + ex.getMessage()); } - @Test - void crossCompileFreesNativeMemory() { - for (int i = 0; i < 200; i++) { - final ByteBuffer spirv = compile(SpirvTestShaders.VERTEX_GLSL, Shaderc.shaderc_vertex_shader); - try { - ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_VERTEX_SHADER); - final CrossCompileCache.Output out = DxbcCrossCompile.compile(spirv, GL20.GL_VERTEX_SHADER); - MemoryUtil.memFree(out.code()); - } finally { - MemoryUtil.memFree(spirv); - } - } - } - @Test void diskCacheHitReturnsStoredBlob(@TempDir Path dir) { ShaderDiskCache.configure(dir, "t"); Reflect.>get(cache(), "cache").clear(); - final ByteBuffer spirv = compile(SpirvTestShaders.VERTEX_GLSL, Shaderc.shaderc_vertex_shader); + final ByteBuffer spirv = compileOrFail(SpirvTestShaders.VERTEX_GLSL, Shaderc.shaderc_vertex_shader); try { ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_VERTEX_SHADER); final byte[] heap = ShaderCacheIO.toHeap(spirv); @@ -159,12 +119,4 @@ class DxbcCompileTest { assertEquals(DXBC_MAGIC_B, view.get(2), "DXBC magic byte 2"); assertEquals(DXBC_MAGIC_C, view.get(3), "DXBC magic byte 3"); } - - private static ByteBuffer compile(String source, int shaderKind) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, shaderKind, "test_shader", SpirvCompiler.Options.vulkanForced460Core()); - if (r.spirv() == null) { - fail("Shader compilation failed: " + r.error()); - } - return r.spirv(); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/dxbc/DxbcHlslSpacesTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/dxbc/DxbcHlslSpacesTest.java index 866187e16..93e963fa2 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/dxbc/DxbcHlslSpacesTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/dxbc/DxbcHlslSpacesTest.java @@ -1,19 +1,23 @@ package com.gtnewhorizons.angelica.sdlgpu.shader.dxbc; import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL43; import org.lwjgl.system.MemoryUtil; import org.lwjgl.util.shaderc.Shaderc; import java.nio.ByteBuffer; +import java.util.List; +import java.util.stream.Stream; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; class DxbcHlslSpacesTest { @@ -43,40 +47,31 @@ class DxbcHlslSpacesTest { } """; - @Test - void vsSpaceMapping_ubosAtSpace1_samplersAtSpace0() { - final ByteBuffer spirv = compile(VS_WITH_UBO_AND_SAMPLER, Shaderc.shaderc_vertex_shader); - try { - ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_VERTEX_SHADER); - final String hlsl = DxbcCrossCompile.compileToHlsl(spirv, GL20.GL_VERTEX_SHADER); + static Stream stages() { + return Stream.of( + Arguments.of("VS: UBO at space1", VS_WITH_UBO_AND_SAMPLER, Shaderc.shaderc_vertex_shader, GL20.GL_VERTEX_SHADER, + List.of("space1"), List.of("space2", "space3")), + Arguments.of("FS: sampler at space2, UBO at space3", FS_WITH_UBO_AND_SAMPLER, Shaderc.shaderc_fragment_shader, GL20.GL_FRAGMENT_SHADER, + List.of("space2", "space3"), List.of("space1"))); + } - assertTrue(hlsl.contains("space1"), "VS HLSL must reference space1 for the UBO. HLSL:\n" + hlsl); - assertFalse(hlsl.contains("space2"), "VS HLSL must not reference space2. HLSL:\n" + hlsl); - assertFalse(hlsl.contains("space3"), "VS HLSL must not reference space3. HLSL:\n" + hlsl); + @ParameterizedTest(name = "{0}") + @MethodSource("stages") + void stageUsesOnlyItsOwnRegisterSpaces(String name, String glsl, int shadercKind, int glStage, List present, List absent) { + final ByteBuffer spirv = compileOrFail(glsl, shadercKind); + try { + ShaderManager.remapSpirvForSDLGPU(spirv, glStage); + final String hlsl = DxbcCrossCompile.compileToHlsl(spirv, glStage); + for (String space : present) assertTrue(hlsl.contains(space), name + ": HLSL must reference " + space + ". HLSL:\n" + hlsl); + for (String space : absent) assertFalse(hlsl.contains(space), name + ": HLSL must not reference " + space + ". HLSL:\n" + hlsl); } finally { MemoryUtil.memFree(spirv); } } - @Test - void fsSpaceMapping_ubosAtSpace3_samplersAtSpace2() { - final ByteBuffer spirv = compile(FS_WITH_UBO_AND_SAMPLER, Shaderc.shaderc_fragment_shader); - try { - ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_FRAGMENT_SHADER); - final String hlsl = DxbcCrossCompile.compileToHlsl(spirv, GL20.GL_FRAGMENT_SHADER); - - assertTrue(hlsl.contains("space2"), "FS HLSL must reference space2 for the sampler/SRV. HLSL:\n" + hlsl); - assertTrue(hlsl.contains("space3"), "FS HLSL must reference space3 for the UBO. HLSL:\n" + hlsl); - assertFalse(hlsl.contains("space1"), "FS HLSL must not reference space1. HLSL:\n" + hlsl); - } finally { - MemoryUtil.memFree(spirv); - } - } - - @Test void ldsMultiMipCompute_hlslHasGroupsharedAndBarrier() { - final ByteBuffer spirv = compile(SpirvTestShaders.LDS_MULTI_MIP_GLSL, Shaderc.shaderc_compute_shader); + final ByteBuffer spirv = compileOrFail(SpirvTestShaders.LDS_MULTI_MIP_GLSL, Shaderc.shaderc_compute_shader); try { ShaderManager.remapSpirvForComputeSDLGPU(spirv); final String hlsl = DxbcCrossCompile.compileToHlsl(spirv, GL43.GL_COMPUTE_SHADER); @@ -86,13 +81,4 @@ class DxbcHlslSpacesTest { MemoryUtil.memFree(spirv); } } - - private static ByteBuffer compile(String source, int shaderKind) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, shaderKind, "hlsl_spaces_test", - SpirvCompiler.Options.vulkanForced460Core()); - if (r.spirv() == null) { - fail("shader compile failed: " + r.error()); - } - return r.spirv(); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/msl/FragmentStorageTextureSlotTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/msl/FragmentStorageTextureSlotTest.java index 8369edf88..53340071c 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/msl/FragmentStorageTextureSlotTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/msl/FragmentStorageTextureSlotTest.java @@ -1,11 +1,12 @@ package com.gtnewhorizons.angelica.sdlgpu.shader.msl; import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager; import com.gtnewhorizons.angelica.sdlgpu.shader.cross.CrossCompileCache; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.lwjgl.opengl.GL20; import org.lwjgl.system.MemoryUtil; import org.lwjgl.util.shaderc.Shaderc; @@ -16,6 +17,7 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -47,14 +49,13 @@ class FragmentStorageTextureSlotTest { return Integer.parseInt(m.group(1)); } - private void assertSlots(int samplerCount) { + @ParameterizedTest(name = "{0} samplers") + @ValueSource(ints = {0, 1, 3}) + void storageTextureSitsAfterTheSamplers(int samplerCount) { final GlslVulkanPreprocess.Result pre = GlslVulkanPreprocess.run(fragmentSource(samplerCount), GL20.GL_FRAGMENT_SHADER, "storage-slot-s" + samplerCount, true); final String src = pre != null ? pre.rewrittenSource() : fragmentSource(samplerCount); - final SpirvCompiler.Result r = SpirvCompiler.compile(src, - Shaderc.shaderc_fragment_shader, "storage-slot-s" + samplerCount, SpirvCompiler.Options.vulkanForced460Core()); - if (r.spirv() == null) fail("compile failed: " + r.error() + "\nsource:\n" + src); - final ByteBuffer spirv = r.spirv(); + final ByteBuffer spirv = compileOrFail(src, Shaderc.shaderc_fragment_shader); try { ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_FRAGMENT_SHADER); final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL20.GL_FRAGMENT_SHADER, false); @@ -77,10 +78,6 @@ class FragmentStorageTextureSlotTest { } } - @Test void storageTextureAfterZeroSamplers() { assertSlots(0); } - @Test void storageTextureAfterOneSampler() { assertSlots(1); } - @Test void storageTextureAfterThreeSamplers() { assertSlots(3); } - @Test void multipleStorageImagesFollowReflectionOrder() throws Exception { final String src = String.join("\n", "#version 460 core", diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/msl/MslCrossCompileTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/msl/MslCrossCompileTest.java index 9474835af..e7c307e45 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/msl/MslCrossCompileTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/shader/msl/MslCrossCompileTest.java @@ -3,8 +3,8 @@ package com.gtnewhorizons.angelica.sdlgpu.shader.msl; import com.gtnewhorizons.angelica.glsm.shader.GlslVulkanPreprocess; import com.gtnewhorizons.angelica.glsm.shader.ShaderCacheIO; import com.gtnewhorizons.angelica.glsm.shader.ShaderDiskCache; -import com.gtnewhorizons.angelica.glsm.shader.SpirvCompiler; import com.gtnewhorizons.angelica.glsm.testutil.Reflect; +import com.gtnewhorizons.angelica.glsm.testutil.TestPaths; import com.gtnewhorizons.angelica.sdlgpu.shader.ShaderManager; import com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders; import com.gtnewhorizons.angelica.sdlgpu.shader.cross.CrossCompileCache; @@ -12,27 +12,29 @@ import com.gtnewhorizons.angelica.sdlgpu.shader.cross.CrossCompileUtil; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL43; import org.lwjgl.system.MemoryUtil; import org.lwjgl.util.shaderc.Shaderc; -import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.nio.file.Path; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Stream; +import static com.gtnewhorizons.angelica.sdlgpu.shader.SpirvTestShaders.compileOrFail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; /** * Tests SPIR-V → MSL cross-compilation for the macOS/Metal backend. @@ -46,11 +48,9 @@ class MslCrossCompileTest { Reflect.>get(cache(), "cache").clear(); } - - @Test void vertexShaderCrossCompiles() { - final ByteBuffer spirv = compile(SpirvTestShaders.VERTEX_GLSL, Shaderc.shaderc_vertex_shader); + final ByteBuffer spirv = compileOrFail(SpirvTestShaders.VERTEX_GLSL, Shaderc.shaderc_vertex_shader); try { ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_VERTEX_SHADER); final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL20.GL_VERTEX_SHADER, false); @@ -73,7 +73,7 @@ class MslCrossCompileTest { @Test void fragmentShaderCrossCompiles() { - final ByteBuffer spirv = compile(SpirvTestShaders.FRAGMENT_GLSL, Shaderc.shaderc_fragment_shader); + final ByteBuffer spirv = compileOrFail(SpirvTestShaders.FRAGMENT_GLSL, Shaderc.shaderc_fragment_shader); try { ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_FRAGMENT_SHADER); final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL20.GL_FRAGMENT_SHADER, false); @@ -109,10 +109,9 @@ class MslCrossCompileTest { } """; - @Test void ldsMultiMipComputeCrossCompiles() { - final ByteBuffer spirv = compile(SpirvTestShaders.LDS_MULTI_MIP_GLSL, Shaderc.shaderc_compute_shader); + final ByteBuffer spirv = compileOrFail(SpirvTestShaders.LDS_MULTI_MIP_GLSL, Shaderc.shaderc_compute_shader); try { ShaderManager.remapSpirvForComputeSDLGPU(spirv); final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL43.GL_COMPUTE_SHADER, false); @@ -140,11 +139,9 @@ class MslCrossCompileTest { * concrete texture type, so verify SPIRV-Cross resolves it rather than emitting an unusable access-less image. */ @Test - void clearImage3dComputeCrossCompiles() throws IOException { - final String glsl = Files.readString( - Path.of("..", "src", "main", "resources", "assets", "angelica", "shaders", "sdlgpu", "clear_image3d.csh"), - StandardCharsets.UTF_8); - final ByteBuffer spirv = compile(glsl, Shaderc.shaderc_compute_shader); + void clearImage3dComputeCrossCompiles() { + final String glsl = TestPaths.readString("src/main/resources/assets/angelica/shaders/sdlgpu/clear_image3d.csh"); + final ByteBuffer spirv = compileOrFail(glsl, Shaderc.shaderc_compute_shader); try { ShaderManager.remapSpirvForComputeSDLGPU(spirv); final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL43.GL_COMPUTE_SHADER, false); @@ -165,7 +162,7 @@ class MslCrossCompileTest { @Test void chunkCullComputeCrossCompiles() { - final ByteBuffer spirv = compile(CHUNK_CULL_GLSL, Shaderc.shaderc_compute_shader); + final ByteBuffer spirv = compileOrFail(CHUNK_CULL_GLSL, Shaderc.shaderc_compute_shader); try { ShaderManager.remapSpirvForComputeSDLGPU(spirv); final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL43.GL_COMPUTE_SHADER, false); @@ -200,7 +197,7 @@ class MslCrossCompileTest { @Test void vertexShaderWritingStorageImageStillReturnsPosition() { - final ByteBuffer spirv = compile(VERTEX_WRITES_IMAGE_GLSL, Shaderc.shaderc_vertex_shader); + final ByteBuffer spirv = compileOrFail(VERTEX_WRITES_IMAGE_GLSL, Shaderc.shaderc_vertex_shader); try { ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_VERTEX_SHADER); final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL20.GL_VERTEX_SHADER, false); @@ -234,7 +231,7 @@ class MslCrossCompileTest { void cppKeywordLocalIsRenamedBeforeMsl() { final GlslVulkanPreprocess.Result pre = GlslVulkanPreprocess.run(CPP_KEYWORD_LOCAL_GLSL, GL20.GL_FRAGMENT_SHADER, "test", true); assertNotNull(pre); - final ByteBuffer spirv = compile(pre.rewrittenSource(), Shaderc.shaderc_fragment_shader); + final ByteBuffer spirv = compileOrFail(pre.rewrittenSource(), Shaderc.shaderc_fragment_shader); try { ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_FRAGMENT_SHADER); final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL20.GL_FRAGMENT_SHADER, false); @@ -250,20 +247,6 @@ class MslCrossCompileTest { } } - @Test - void crossCompileFreesNativeMemory() { - for (int i = 0; i < 200; i++) { - final ByteBuffer spirv = compile(SpirvTestShaders.VERTEX_GLSL, Shaderc.shaderc_vertex_shader); - try { - ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_VERTEX_SHADER); - final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL20.GL_VERTEX_SHADER, false); - MemoryUtil.memFree(out.code()); - } finally { - MemoryUtil.memFree(spirv); - } - } - } - // Mimics the shape of RwImageStoreExtractor's chunk-vertex compute output: // - loose uniforms (consolidated by shaderc into a default UBO) // - readonly SSBO at binding 9 (_VgVbuf) @@ -295,11 +278,6 @@ class MslCrossCompileTest { } """; - @Test - void chunkComputeWithImageAtomic_mslBufferSlotsAreUnique() { - assertMslBufferSlotsUnique(CHUNK_COMPUTE_WITH_ATOMIC_IMAGE_GLSL); - } - private static final String COMPOSITE_VSH_COMPUTE_WITH_ATOMIC_IMAGE_GLSL = """ #version 460 core layout(local_size_x = 64) in; @@ -327,11 +305,6 @@ class MslCrossCompileTest { } """; - @Test - void compositeVshComputeWithImageAtomic_mslBufferSlotsAreUnique() { - assertMslBufferSlotsUnique(COMPOSITE_VSH_COMPUTE_WITH_ATOMIC_IMAGE_GLSL); - } - // Mirrors composite-fsh: 2D dispatch + _vg_target_size uniform + gl_FragCoord shim. private static final String COMPOSITE_FSH_COMPUTE_WITH_ATOMIC_IMAGE_GLSL = """ #version 460 core @@ -351,11 +324,6 @@ class MslCrossCompileTest { } """; - @Test - void compositeFshComputeWithImageAtomic_mslBufferSlotsAreUnique() { - assertMslBufferSlotsUnique(COMPOSITE_FSH_COMPUTE_WITH_ATOMIC_IMAGE_GLSL); - } - private static final String CHUNK_COMPUTE_WITH_INLINE_ATOMIC_GLSL = """ #version 460 core layout(local_size_x = 64) in; @@ -374,12 +342,21 @@ class MslCrossCompileTest { @Test void chunkComputeWithInlineAtomic_shadercAccepts() { - final ByteBuffer spirv = compile(CHUNK_COMPUTE_WITH_INLINE_ATOMIC_GLSL, Shaderc.shaderc_compute_shader); + final ByteBuffer spirv = compileOrFail(CHUNK_COMPUTE_WITH_INLINE_ATOMIC_GLSL, Shaderc.shaderc_compute_shader); MemoryUtil.memFree(spirv); } - private static void assertMslBufferSlotsUnique(String glsl) { - final ByteBuffer spirv = compile(glsl, Shaderc.shaderc_compute_shader); + static Stream computeShadersWithImageAtomics() { + return Stream.of( + Arguments.of("chunk compute", CHUNK_COMPUTE_WITH_ATOMIC_IMAGE_GLSL), + Arguments.of("composite vsh compute", COMPOSITE_VSH_COMPUTE_WITH_ATOMIC_IMAGE_GLSL), + Arguments.of("composite fsh compute", COMPOSITE_FSH_COMPUTE_WITH_ATOMIC_IMAGE_GLSL)); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("computeShadersWithImageAtomics") + void computeWithImageAtomic_mslBufferSlotsAreUnique(String name, String glsl) { + final ByteBuffer spirv = compileOrFail(glsl, Shaderc.shaderc_compute_shader); try { ShaderManager.remapSpirvForComputeSDLGPU(spirv); final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL43.GL_COMPUTE_SHADER, false); @@ -426,15 +403,11 @@ class MslCrossCompileTest { } @Test - void realCullingShadersCrossCompile() throws IOException { + void realCullingShadersCrossCompile() { final String[] names = { "chunk_cull.csh" }; for (String name : names) { - Path p = Path.of("src/main/resources/assets/angelica/shaders/culling/" + name); - if (!Files.exists(p)) { - p = Path.of("../src/main/resources/assets/angelica/shaders/culling/" + name); - } - final String glsl = Files.readString(p); - final ByteBuffer spirv = compile(glsl, Shaderc.shaderc_compute_shader); + final String glsl = TestPaths.readString("src/main/resources/assets/angelica/shaders/culling/" + name); + final ByteBuffer spirv = compileOrFail(glsl, Shaderc.shaderc_compute_shader); try { ShaderManager.remapSpirvForComputeSDLGPU(spirv); final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL43.GL_COMPUTE_SHADER, false); @@ -453,7 +426,7 @@ class MslCrossCompileTest { void diskCacheHitReturnsStoredBlob(@TempDir Path dir) { ShaderDiskCache.configure(dir, "t"); Reflect.>get(cache(), "cache").clear(); - final ByteBuffer spirv = compile(SpirvTestShaders.VERTEX_GLSL, Shaderc.shaderc_vertex_shader); + final ByteBuffer spirv = compileOrFail(SpirvTestShaders.VERTEX_GLSL, Shaderc.shaderc_vertex_shader); try { ShaderManager.remapSpirvForSDLGPU(spirv, GL20.GL_VERTEX_SHADER); final byte[] heap = ShaderCacheIO.toHeap(spirv); @@ -511,7 +484,7 @@ class MslCrossCompileTest { @Test void textureAtomicsLeaveRasterStagesAtMsl22() { - final ByteBuffer spirv = compile(FRAGMENT_IMAGE_ATOMIC_GLSL, Shaderc.shaderc_fragment_shader); + final ByteBuffer spirv = compileOrFail(FRAGMENT_IMAGE_ATOMIC_GLSL, Shaderc.shaderc_fragment_shader); try { assertTrue(CrossCompileUtil.usesImageAtomics(spirv)); final String base = fragmentMsl(spirv, false); @@ -534,7 +507,7 @@ class MslCrossCompileTest { } private static String computeMsl(String glsl, boolean textureAtomics) { - final ByteBuffer spirv = compile(glsl, Shaderc.shaderc_compute_shader); + final ByteBuffer spirv = compileOrFail(glsl, Shaderc.shaderc_compute_shader); try { ShaderManager.remapSpirvForComputeSDLGPU(spirv); final CrossCompileCache.Output out = MslCrossCompile.compile(spirv, GL43.GL_COMPUTE_SHADER, textureAtomics); @@ -550,8 +523,8 @@ class MslCrossCompileTest { @Test void onlyShadersWithImageAtomicsAreDetected() { - final ByteBuffer atomic = compile(IMAGE_ATOMIC_GLSL, Shaderc.shaderc_compute_shader); - final ByteBuffer plain = compile(CHUNK_CULL_GLSL, Shaderc.shaderc_compute_shader); + final ByteBuffer atomic = compileOrFail(IMAGE_ATOMIC_GLSL, Shaderc.shaderc_compute_shader); + final ByteBuffer plain = compileOrFail(CHUNK_CULL_GLSL, Shaderc.shaderc_compute_shader); try { assertTrue(CrossCompileUtil.usesImageAtomics(atomic)); assertFalse(CrossCompileUtil.usesImageAtomics(plain)); @@ -569,13 +542,4 @@ class MslCrossCompileTest { slice.get(bytes, 0, len); return new String(bytes, StandardCharsets.UTF_8); } - - private static ByteBuffer compile(String source, int shaderKind) { - final SpirvCompiler.Result r = SpirvCompiler.compile(source, shaderKind, "test_shader", - SpirvCompiler.Options.vulkanForced460Core()); - if (r.spirv() == null) { - fail("Shader compilation failed: " + r.error()); - } - return r.spirv(); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/AutoVaoBindTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/AutoVaoBindTest.java deleted file mode 100644 index adce78e21..000000000 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/AutoVaoBindTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.gtnewhorizons.angelica.sdlgpu.threading; - -import org.junit.jupiter.api.Test; - -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.junit.jupiter.api.Assertions.*; - -class AutoVaoBindTest { - - private static final class Gate { - final ThreadLocal autoBound = ThreadLocal.withInitial(() -> Boolean.FALSE); - final Thread mainThread; - Gate(Thread main) { this.mainThread = main; } - - boolean wouldAutoBind() { - if (Thread.currentThread() == mainThread) return false; - if (autoBound.get()) return false; - autoBound.set(Boolean.TRUE); - return true; - } - } - - @Test - void mainThreadDoesNotAutoBind() { - final Gate g = new Gate(Thread.currentThread()); - assertFalse(g.wouldAutoBind()); - assertFalse(g.wouldAutoBind(), "still false on subsequent calls"); - } - - @Test - void nonMainThreadAutoBindsOnce() throws Exception { - final Gate g = new Gate(Thread.currentThread()); - final AtomicBoolean firstCall = new AtomicBoolean(); - final AtomicBoolean secondCall = new AtomicBoolean(); - final Thread t = new Thread(() -> { - firstCall.set(g.wouldAutoBind()); - secondCall.set(g.wouldAutoBind()); - }, "non-main"); - t.start(); - t.join(); - assertTrue(firstCall.get(), "first call from non-main thread auto-binds"); - assertFalse(secondCall.get(), "second call does not"); - } - - @Test - void distinctNonMainThreadsEachAutoBind() throws Exception { - final Gate g = new Gate(Thread.currentThread()); - final AtomicBoolean a = new AtomicBoolean(); - final AtomicBoolean b = new AtomicBoolean(); - final Thread tA = new Thread(() -> a.set(g.wouldAutoBind()), "non-main-a"); - final Thread tB = new Thread(() -> b.set(g.wouldAutoBind()), "non-main-b"); - tA.start(); tB.start(); tA.join(); tB.join(); - assertTrue(a.get()); - assertTrue(b.get()); - } -} diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/ContextStateNativeBufsAllocatedTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/ContextStateNativeBufsAllocatedTest.java index 08ae62d9b..71653f6f8 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/ContextStateNativeBufsAllocatedTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/ContextStateNativeBufsAllocatedTest.java @@ -3,8 +3,6 @@ package com.gtnewhorizons.angelica.sdlgpu.threading; import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; import org.junit.jupiter.api.Test; -import java.util.concurrent.atomic.AtomicReference; - import static org.junit.jupiter.api.Assertions.*; class ContextStateNativeBufsAllocatedTest { @@ -18,19 +16,4 @@ class ContextStateNativeBufsAllocatedTest { assertNotNull(cs.cachedScissor, "cachedScissor must be allocated"); assertNotNull(cs.cachedBlendColor, "cachedBlendColor must be allocated"); } - - @Test - void nonMainThreadContextStateHasAllNativeBufsAllocated() throws Exception { - final AtomicReference result = new AtomicReference<>(); - final Thread t = new Thread(() -> result.set(new ContextState()), "non-main-ctxstate"); - t.start(); - t.join(); - final ContextState cs = result.get(); - assertNotNull(cs); - assertNotEquals(0L, cs.vboBindingsAddr); - assertNotNull(cs.cachedViewport); - assertNotNull(cs.cachedScissor); - assertNotNull(cs.cachedBlendColor); - assertNotNull(cs.eboBinding); - } } diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/PerThreadContextStateTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/PerThreadContextStateTest.java index f69cfe120..dac78fc3e 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/PerThreadContextStateTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/PerThreadContextStateTest.java @@ -3,65 +3,10 @@ package com.gtnewhorizons.angelica.sdlgpu.threading; import com.gtnewhorizons.angelica.sdlgpu.frame.ContextState; import org.junit.jupiter.api.Test; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - import static org.junit.jupiter.api.Assertions.*; class PerThreadContextStateTest { - @Test - void twoThreadsHaveIndependentBoundProgram() throws Exception { - final ThreadLocal tl = ThreadLocal.withInitial(ContextState::new); - final CountDownLatch start = new CountDownLatch(1); - final CountDownLatch step1 = new CountDownLatch(2); - final CountDownLatch step2 = new CountDownLatch(1); - final AtomicInteger seenA = new AtomicInteger(-1); - final AtomicInteger seenB = new AtomicInteger(-1); - final AtomicReference failure = new AtomicReference<>(); - - final Thread a = new Thread(() -> { - try { - start.await(); - tl.get().boundProgram = 100; - tl.get().boundTextures[0] = 1001; - tl.get().viewportX = 10.0f; tl.get().viewportY = 20.0f; - step1.countDown(); - step2.await(); - seenA.set(tl.get().boundProgram); - assertEquals(1001, tl.get().boundTextures[0]); - assertEquals(10.0f, tl.get().viewportX); - } catch (Throwable e) { failure.compareAndSet(null, e); } - }, "ctxstate-a"); - - final Thread b = new Thread(() -> { - try { - start.await(); - tl.get().boundProgram = 200; - tl.get().boundTextures[0] = 2002; - tl.get().viewportX = 30.0f; tl.get().viewportY = 40.0f; - step1.countDown(); - step2.await(); - seenB.set(tl.get().boundProgram); - assertEquals(2002, tl.get().boundTextures[0]); - assertEquals(30.0f, tl.get().viewportX); - } catch (Throwable e) { failure.compareAndSet(null, e); } - }, "ctxstate-b"); - - a.start(); b.start(); - start.countDown(); - assertTrue(step1.await(5, TimeUnit.SECONDS)); - step2.countDown(); - a.join(TimeUnit.SECONDS.toMillis(5)); - b.join(TimeUnit.SECONDS.toMillis(5)); - - assertNull(failure.get(), () -> "race: " + failure.get()); - assertEquals(100, seenA.get(), "thread A should see its own boundProgram"); - assertEquals(200, seenB.get(), "thread B should see its own boundProgram"); - } - @Test void freshContextStateHasIdentityAttribBinding() { final ContextState cs = new ContextState(); diff --git a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/PipelineCacheConcurrentBuildTest.java b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/PipelineCacheConcurrentBuildTest.java index 240f9acdc..f5636adb9 100644 --- a/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/PipelineCacheConcurrentBuildTest.java +++ b/sdl-gpu/src/test/java/com/gtnewhorizons/angelica/sdlgpu/threading/PipelineCacheConcurrentBuildTest.java @@ -1,10 +1,10 @@ package com.gtnewhorizons.angelica.sdlgpu.threading; +import com.gtnewhorizons.angelica.glsm.testutil.TestThreads; import com.gtnewhorizons.angelica.sdlgpu.pipeline.PipelineStore; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -18,21 +18,20 @@ class PipelineCacheConcurrentBuildTest { final int threads = 8; final int keysPerThread = 500; final CountDownLatch start = new CountDownLatch(1); - final Thread[] all = new Thread[threads]; + final TestThreads.Worker[] all = new TestThreads.Worker[threads]; for (int t = 0; t < threads; t++) { final int tid = t; - all[t] = new Thread(() -> { - try { start.await(); } catch (InterruptedException ignored) { return; } + all[t] = TestThreads.start("build-" + t, () -> { + start.await(); for (int i = 0; i < keysPerThread; i++) { final long key = ((long) tid << 16) | i; final long p = store.getOrBuild(key, () -> { builds.incrementAndGet(); return key * 1000L + 1; }); assertEquals(key * 1000L + 1, p); } - }, "build-" + t); - all[t].start(); + }); } start.countDown(); - for (Thread th : all) th.join(TimeUnit.SECONDS.toMillis(10)); + for (TestThreads.Worker w : all) w.join(); assertEquals(threads * keysPerThread, builds.get(), "Each distinct key should be built exactly once"); } @@ -44,17 +43,16 @@ class PipelineCacheConcurrentBuildTest { final long key = 0xDEADBEEFL; final long[] results = new long[threads]; final CountDownLatch start = new CountDownLatch(1); - final Thread[] all = new Thread[threads]; + final TestThreads.Worker[] all = new TestThreads.Worker[threads]; for (int t = 0; t < threads; t++) { final int tid = t; - all[t] = new Thread(() -> { - try { start.await(); } catch (InterruptedException ignored) { return; } + all[t] = TestThreads.start("race-" + t, () -> { + start.await(); results[tid] = store.getOrBuild(key, () -> { builds.incrementAndGet(); return key * 1000L + 1; }); - }, "race-" + t); - all[t].start(); + }); } start.countDown(); - for (Thread th : all) th.join(TimeUnit.SECONDS.toMillis(10)); + for (TestThreads.Worker w : all) w.join(); assertEquals(1, builds.get(), "Same key must build exactly once across threads"); for (int i = 0; i < threads; i++) { assertEquals(results[0], results[i], "All threads must see the same pipeline handle"); diff --git a/src/test/java/com/gtnewhorizons/angelica/api/GLRedirectsTest.java b/src/test/java/com/gtnewhorizons/angelica/api/GLRedirectsTest.java index 005af05e8..576c62d10 100644 --- a/src/test/java/com/gtnewhorizons/angelica/api/GLRedirectsTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/api/GLRedirectsTest.java @@ -6,7 +6,6 @@ import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -70,24 +69,10 @@ class GLRedirectsTest { "GL42, glTexStorage2D, glTexStorage2D", "GL43, glDispatchCompute, glDispatchCompute", "GL44, glBufferStorage, glBufferStorage", - }) - void glVersionClassMethodRedirects(String className, String method, String expected) { - assertEquals(expected, GLRedirects.getTargetMethodName(className + "." + method)); - } - - @ParameterizedTest(name = "{0}.{1} -> {2}") - @CsvSource({ "GL11C, glDrawArrays, glDrawArrays", "GL20C, glUseProgram, glUseProgram", "GL30C, glBindFramebuffer, glBindFramebuffer", "GL33C, glBindSampler, glBindSampler", - }) - void cVariantsUseGLPrefixMap(String className, String method, String expected) { - assertEquals(expected, GLRedirects.getTargetMethodName(className + "." + method)); - } - - @ParameterizedTest(name = "{0}.{1} -> {2}") - @CsvSource({ "EXTBlendFuncSeparate, glBlendFuncSeparateEXT, tryBlendFuncSeparate", "ARBMultitexture, glActiveTextureARB, glActiveTextureARB", "ARBShaderObjects, glUseProgramObjectARB, glUseProgram", @@ -116,7 +101,7 @@ class GLRedirectsTest { "Project, gluPerspective, gluPerspective", "Project, gluPickMatrix, gluPickMatrix", }) - void namedClassMethodRedirects(String className, String method, String expected) { + void classMethodRedirects(String className, String method, String expected) { assertEquals(expected, GLRedirects.getTargetMethodName(className + "." + method)); } @@ -178,9 +163,4 @@ class GLRedirectsTest { assertTrue(result.contains("GLStateManager.glTranslatef(1,2,3)"), "glTranslatef not redirected: " + result); assertTrue(result.contains("GLStateManager.glEnable(GL11.GL_BLEND)"), "glEnable not redirected or GL_BLEND mangled: " + result); } - - @Test - void patternIsNotNull() { - assertNotNull(GLRedirects.getMethodRedirectPattern()); - } } diff --git a/src/test/java/com/gtnewhorizons/angelica/client/ChatLineFormattedCacheTest.java b/src/test/java/com/gtnewhorizons/angelica/client/ChatLineFormattedCacheTest.java deleted file mode 100644 index 153cf69fa..000000000 --- a/src/test/java/com/gtnewhorizons/angelica/client/ChatLineFormattedCacheTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.gtnewhorizons.angelica.client; - -import com.gtnewhorizons.angelica.mixins.interfaces.ChatLineFormattedAccessor; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; - -class ChatLineFormattedCacheTest { - - private static final class Line implements ChatLineFormattedAccessor { - private String formatted; - private long formattedEpoch = Long.MIN_VALUE; - - @Override - public String angelica$getFormatted(long epoch) { - return formattedEpoch == epoch ? formatted : null; - } - - @Override - public void angelica$setFormatted(String formatted, long epoch) { - this.formatted = formatted; - this.formattedEpoch = epoch; - } - } - - @Test - void aFreshLineHasNothingCached() { - assertNull(new Line().angelica$getFormatted(0L)); - } - - @Test - void theSameEpochReturnsTheCachedInstance() { - final Line line = new Line(); - final String formatted = "§ahello"; - line.angelica$setFormatted(formatted, 1234L); - assertSame(formatted, line.angelica$getFormatted(1234L)); - } - - @Test - void aTranslationEpochChangeInvalidates() { - final Line line = new Line(); - line.angelica$setFormatted("§aold locale", 1234L); - assertNull(line.angelica$getFormatted(5678L), "a language reload must force a rebuild"); - - line.angelica$setFormatted("§anew locale", 5678L); - assertEquals("§anew locale", line.angelica$getFormatted(5678L)); - assertNull(line.angelica$getFormatted(1234L), "the stale epoch never comes back"); - } - - @Test - void obfuscatedCodesAreCachedVerbatim() { - final Line line = new Line(); - final String formatted = "§kMAGIC§r plain"; - line.angelica$setFormatted(formatted, 7L); - assertEquals(formatted, line.angelica$getFormatted(7L)); - } - - @Test - void zeroIsAUsableEpoch() { - final Line line = new Line(); - line.angelica$setFormatted("x", 0L); - assertEquals("x", line.angelica$getFormatted(0L)); - } -} diff --git a/src/test/java/com/gtnewhorizons/angelica/client/font/ColorCodeUtilsTest.java b/src/test/java/com/gtnewhorizons/angelica/client/font/ColorCodeUtilsTest.java index 6d7acb024..f71976bbf 100644 --- a/src/test/java/com/gtnewhorizons/angelica/client/font/ColorCodeUtilsTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/client/font/ColorCodeUtilsTest.java @@ -4,6 +4,11 @@ import com.gtnewhorizons.angelica.config.AngelicaConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; @@ -27,30 +32,20 @@ class ColorCodeUtilsTest { ColorCodeUtils.setConversionSuppressor(null); } - @Test - void convertsHexShorthand() { - assertEquals("§x§f§f§0§0§0§0Hello", ColorCodeUtils.convertAmpersandToSectionX("&#ff0000Hello")); + static Stream conversions() { + return Stream.of( + Arguments.of("hex shorthand", "&#ff0000Hello", "§x§f§f§0§0§0§0Hello"), + Arguments.of("hex digit case is preserved", "&#FF0000Hi", "§x§F§F§0§0§0§0Hi"), + Arguments.of("single codes", "&cred &lbold &rreset &qrainbow", "§cred §lbold §rreset §qrainbow"), + Arguments.of("gradient form", "&g&#ff0000�ffGrad", "§g§x§f§f§0§0§0§0§x§0§0§0§0§f§fGrad"), + Arguments.of("bare gradient prefix stays literal", "&gplain", "&gplain"), + Arguments.of("escaped ampersand becomes the sentinel", "\\&c", ColorCodeUtils.ESCAPED_AMPERSAND + "c")); } - @Test - void preservesHexDigitCase() { - assertEquals("§x§F§F§0§0§0§0Hi", ColorCodeUtils.convertAmpersandToSectionX("&#FF0000Hi")); - } - - @Test - void convertsSingleCodes() { - assertEquals("§cred §lbold §rreset §qrainbow", ColorCodeUtils.convertAmpersandToSectionX("&cred &lbold &rreset &qrainbow")); - } - - @Test - void convertsGradientForm() { - assertEquals("§g§x§f§f§0§0§0§0§x§0§0§0§0§f§fGrad", - ColorCodeUtils.convertAmpersandToSectionX("&g&#ff0000�ffGrad")); - } - - @Test - void bareGradientPrefixStaysLiteral() { - assertEquals("&gplain", ColorCodeUtils.convertAmpersandToSectionX("&gplain")); + @ParameterizedTest(name = "{0}") + @MethodSource("conversions") + void convertsAmpersandForms(String name, String input, String expected) { + assertEquals(expected, ColorCodeUtils.convertAmpersandToSectionX(input)); } @Test @@ -59,12 +54,6 @@ class ColorCodeUtilsTest { assertSame(input, ColorCodeUtils.convertAmpersandToSectionX(input)); } - @Test - void escapedAmpersandBecomesSentinel() { - assertEquals(String.valueOf(ColorCodeUtils.ESCAPED_AMPERSAND) + "c", - ColorCodeUtils.convertAmpersandToSectionX("\\&c")); - } - @Test void disabledConversionReturnsSameInstance() { AngelicaConfig.enableAmpersandConversion = false; diff --git a/src/test/java/com/gtnewhorizons/angelica/client/font/FontBatchMatrixTest.java b/src/test/java/com/gtnewhorizons/angelica/client/font/FontBatchMatrixTest.java deleted file mode 100644 index 3c7207db8..000000000 --- a/src/test/java/com/gtnewhorizons/angelica/client/font/FontBatchMatrixTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.gtnewhorizons.angelica.client.font; - -import org.joml.Matrix4f; -import org.joml.Matrix4fStack; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -class FontBatchMatrixTest { - - private static Matrix4f overlayOrtho() { - return new Matrix4f().ortho(0, 854, 480, 0, 1000, 3000).translate(0, 0, -2000); - } - - @Test - void anUnchangedMatrixDoesNotSegment() { - final Matrix4f cached = overlayOrtho(); - assertTrue(cached.equals(new Matrix4f(cached), 0.0f)); - } - - @Test - void aScaleSegments() { - final Matrix4f cached = overlayOrtho(); - assertFalse(cached.equals(new Matrix4f(cached).scale(0.5f), 0.0f)); - } - - @Test - void aDepthOnlyTranslateSegments() { - final Matrix4f cached = overlayOrtho(); - assertFalse(cached.equals(new Matrix4f(cached).translate(0, 0, 50), 0.0f)); - } - - @Test - void aStackIsComparedByValue() { - final Matrix4f cached = overlayOrtho(); - final Matrix4fStack live = new Matrix4fStack(4); - live.set(cached); - live.pushMatrix(); - assertTrue(cached.equals(live, 0.0f)); - - live.scale(0.5f); - assertFalse(cached.equals(live, 0.0f)); - - live.popMatrix(); - assertTrue(cached.equals(live, 0.0f)); - } -} diff --git a/src/test/java/com/gtnewhorizons/angelica/client/font/FontEffectRegistryTest.java b/src/test/java/com/gtnewhorizons/angelica/client/font/FontEffectRegistryTest.java index a7887e604..eed28baaa 100644 --- a/src/test/java/com/gtnewhorizons/angelica/client/font/FontEffectRegistryTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/client/font/FontEffectRegistryTest.java @@ -46,11 +46,6 @@ class FontEffectRegistryTest { assertTrue(FontEffectRegistry.isRegistered('t')); } - @Test - void bitMatchesLetterIndex() { - assertEquals(1L << ('w' - 'a'), FontEffectRegistry.bit('w')); - } - @Test void unimplementedHooksDoNothing() { assertEquals(0.0f, NO_OP.offsetX(3)); diff --git a/src/test/java/com/gtnewhorizons/angelica/client/font/FontFilterLightmapGLTest.java b/src/test/java/com/gtnewhorizons/angelica/client/font/FontFilterLightmapGLTest.java index c33d290ee..8bb1c1526 100644 --- a/src/test/java/com/gtnewhorizons/angelica/client/font/FontFilterLightmapGLTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/client/font/FontFilterLightmapGLTest.java @@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.client.font; import com.gtnewhorizons.angelica.glsm.GLCoreTest; import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import com.gtnewhorizons.angelica.glsm.testutil.TestPaths; import net.coderbot.iris.uniforms.builtin.BuiltinReplacementUniforms; import org.joml.Matrix4f; @@ -12,7 +13,6 @@ import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL20; import java.nio.ByteBuffer; @@ -54,24 +54,9 @@ class FontFilterLightmapGLTest { if (program != 0) { GLStateManager.glDeleteProgram(program); program = 0; } } - private static int compile(int type, String name) { - final int shader = GLStateManager.glCreateShader(type); - GLStateManager.glShaderSource(shader, TestPaths.readString("src/main/resources/assets/angelica/shaders/" + name)); - GLStateManager.glCompileShader(shader); - assertEquals(GL11.GL_TRUE, GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS), () -> name + " failed to compile:\n" + GLStateManager.glGetShaderInfoLog(shader, 4096)); - return shader; - } - private void buildProgram() { - final int vertex = compile(GL20.GL_VERTEX_SHADER, "fontFilter.vsh"); - final int fragment = compile(GL20.GL_FRAGMENT_SHADER, "fontFilter.fsh"); - program = GLStateManager.glCreateProgram(); - GLStateManager.glAttachShader(program, vertex); - GLStateManager.glAttachShader(program, fragment); - GLStateManager.glLinkProgram(program); - assertEquals(GL11.GL_TRUE, GLStateManager.glGetProgrami(program, GL20.GL_LINK_STATUS), () -> "fontFilter failed to link:\n" + GLStateManager.glGetProgramInfoLog(program, 4096)); - GLStateManager.glDeleteShader(vertex); - GLStateManager.glDeleteShader(fragment); + final String dir = "src/main/resources/assets/angelica/shaders/"; + program = GlTestSupport.program(TestPaths.readString(dir + "fontFilter.vsh"), TestPaths.readString(dir + "fontFilter.fsh")); } private void buildLightmapTexture() { diff --git a/src/test/java/com/gtnewhorizons/angelica/client/font/TextDrawStateGLTest.java b/src/test/java/com/gtnewhorizons/angelica/client/font/TextDrawStateGLTest.java index 4a5a4f1e5..1654d657c 100644 --- a/src/test/java/com/gtnewhorizons/angelica/client/font/TextDrawStateGLTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/client/font/TextDrawStateGLTest.java @@ -5,12 +5,16 @@ import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.StateSet; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.opengl.GL11; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -116,17 +120,13 @@ class TextDrawStateGLTest { assertTrue(GL11.glIsEnabled(GL11.GL_POLYGON_OFFSET_FILL), "driver polygon offset fill"); } - @Test - void fontStateSetRestoresEverythingApplyTouches() throws IllegalAccessException { - assertApplyDoesNotLeak(StateSet.FONT); + static Stream fontStateSets() { + return Stream.of(Arguments.of("FONT", StateSet.FONT), Arguments.of("FONT_PIPELINE", StateSet.FONT_PIPELINE)); } - @Test - void fontPipelineStateSetRestoresEverythingApplyTouches() throws IllegalAccessException { - assertApplyDoesNotLeak(StateSet.FONT_PIPELINE); - } - - private static void assertApplyDoesNotLeak(StateSet set) throws IllegalAccessException { + @ParameterizedTest(name = "{0}") + @MethodSource("fontStateSets") + void fontStateSetRestoresEverythingApplyTouches(String name, StateSet set) throws IllegalAccessException { setNonDefaults(); final TextDrawState moved = capture(); setDefaults(); diff --git a/src/test/java/com/gtnewhorizons/angelica/client/rendering/DeferredDrawBatcherTest.java b/src/test/java/com/gtnewhorizons/angelica/client/rendering/DeferredDrawBatcherTest.java index 1b074536d..e98cdd2ce 100644 --- a/src/test/java/com/gtnewhorizons/angelica/client/rendering/DeferredDrawBatcherTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/client/rendering/DeferredDrawBatcherTest.java @@ -1,44 +1,27 @@ package com.gtnewhorizons.angelica.client.rendering; -import java.util.ArrayList; -import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFlags; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.lwjgl.opengl.GL11; import static org.junit.jupiter.api.Assertions.*; class DeferredDrawBatcherTest { - @Test - void stateKey_roundtrip_basic() { - long key = DeferredDrawBatcher.packStateKey(42, GL11.GL_SRC_ALPHA, GL11.GL_ONE, true, false, true, true); - assertEquals(42, DeferredDrawBatcher.unpackTextureId(key)); - assertEquals(GL11.GL_SRC_ALPHA, DeferredDrawBatcher.unpackSrcRgb(key)); - assertEquals(GL11.GL_ONE, DeferredDrawBatcher.unpackDstRgb(key)); - assertTrue(DeferredDrawBatcher.unpackBlendEnabled(key)); - assertFalse(DeferredDrawBatcher.unpackDepthMask(key)); - } - - @Test - void stateKey_roundtrip_allFlags() { - long key = DeferredDrawBatcher.packStateKey(0, 0, 0, false, true, true, true); - assertEquals(0, DeferredDrawBatcher.unpackTextureId(key)); - assertEquals(0, DeferredDrawBatcher.unpackSrcRgb(key)); - assertEquals(0, DeferredDrawBatcher.unpackDstRgb(key)); - assertFalse(DeferredDrawBatcher.unpackBlendEnabled(key)); - assertTrue(DeferredDrawBatcher.unpackDepthMask(key)); - } - - @Test - void stateKey_roundtrip_largeTextureId() { - // 20-bit texture ID = max 1048575 - int texId = (1 << 20) - 1; - long key = DeferredDrawBatcher.packStateKey(texId, 0x0302, 0x0303, true, true, true, true); - assertEquals(texId, DeferredDrawBatcher.unpackTextureId(key)); - assertEquals(0x0302, DeferredDrawBatcher.unpackSrcRgb(key)); - assertEquals(0x0303, DeferredDrawBatcher.unpackDstRgb(key)); - assertTrue(DeferredDrawBatcher.unpackBlendEnabled(key)); - assertTrue(DeferredDrawBatcher.unpackDepthMask(key)); + @ParameterizedTest(name = "{0}") + @CsvSource({ + "basic, 42, 0x0302, 0x0001, true, false", + "zero fields, 0, 0, 0, false, true", + "largest 20-bit texture id, 1048575, 0x0302, 0x0303, true, true", + }) + void stateKey_roundtrip(String name, int textureId, int srcRgb, int dstRgb, boolean blend, boolean depthMask) { + long key = DeferredDrawBatcher.packStateKey(textureId, srcRgb, dstRgb, blend, depthMask, true, true); + assertEquals(textureId, DeferredDrawBatcher.unpackTextureId(key)); + assertEquals(srcRgb, DeferredDrawBatcher.unpackSrcRgb(key)); + assertEquals(dstRgb, DeferredDrawBatcher.unpackDstRgb(key)); + assertEquals(blend, DeferredDrawBatcher.unpackBlendEnabled(key)); + assertEquals(depthMask, DeferredDrawBatcher.unpackDepthMask(key)); } @Test @@ -50,97 +33,6 @@ class DeferredDrawBatcherTest { assertNotEquals(keyA, keyC, "Different blend funcs should produce different keys"); } - @Test - void grouping_sameKeyEntriesMerged() { - // Verify that entries with the same state key sort together - long keyA = DeferredDrawBatcher.packStateKey(1, 0x0302, 0x0001, true, true, true, true); - long keyB = DeferredDrawBatcher.packStateKey(2, 0x0302, 0x0001, true, true, true, true); - - // Create entries with interleaved keys: A, B, A, B, A - final int flags = VertexFlags.convertToFlags(true, true, false, true); - var entries = new ArrayList(); - entries.add(new DeferredBatchTessellator.DrawRange(keyA, 0, 128, 4, GL11.GL_QUADS, flags)); - entries.add(new DeferredBatchTessellator.DrawRange(keyB, 128, 128, 4, GL11.GL_QUADS, flags)); - entries.add(new DeferredBatchTessellator.DrawRange(keyA, 256, 128, 4, GL11.GL_QUADS, flags)); - entries.add(new DeferredBatchTessellator.DrawRange(keyB, 384, 128, 4, GL11.GL_QUADS, flags)); - entries.add(new DeferredBatchTessellator.DrawRange(keyA, 512, 128, 4, GL11.GL_QUADS, flags)); - - // Sort by state key (same as exitAndFlush) - entries.sort((a, b) -> Long.compare(a.stateKey(), b.stateKey())); - - // Count groups - int groupCount = 0; - int[] groupSizes = new int[2]; - long currentKey = entries.get(0).stateKey(); - int groupSize = 0; - - for (int i = 0; i <= entries.size(); i++) { - if (i == entries.size() || entries.get(i).stateKey() != currentKey) { - groupSizes[groupCount] = groupSize; - groupCount++; - if (i < entries.size()) { - currentKey = entries.get(i).stateKey(); - groupSize = 1; - } - } else { - groupSize++; - } - } - - assertEquals(2, groupCount, "Should have exactly 2 groups"); - // keyA < keyB (texture ID 1 vs 2), so keyA group comes first - assertEquals(3, groupSizes[0], "Key A group should have 3 entries"); - assertEquals(2, groupSizes[1], "Key B group should have 2 entries"); - } - - @Test - void grouping_mergedVertexCountCorrect() { - long key = DeferredDrawBatcher.packStateKey(1, 0x0302, 0x0001, true, true, true, true); - - final int flags = VertexFlags.convertToFlags(true, true, false, true); - var entries = new ArrayList(); - entries.add(new DeferredBatchTessellator.DrawRange(key, 0, 128, 4, GL11.GL_QUADS, flags)); - entries.add(new DeferredBatchTessellator.DrawRange(key, 128, 256, 8, GL11.GL_QUADS, flags)); - entries.add(new DeferredBatchTessellator.DrawRange(key, 384, 128, 4, GL11.GL_QUADS, flags)); - - // Simulate merge within a group: sum vertices and byte lengths - int totalVertices = 0; - int totalBytes = 0; - for (var e : entries) { - totalVertices += e.vertexCount(); - totalBytes += e.byteLength(); - } - - assertEquals(16, totalVertices, "Merged group should have 4+8+4=16 vertices"); - assertEquals(512, totalBytes, "Merged group should have 128+256+128=512 bytes"); - } - - @Test - void grouping_differentDrawModesFormSubgroups() { - long key = DeferredDrawBatcher.packStateKey(1, 0x0302, 0x0001, true, true, true, true); - - final int flags = VertexFlags.convertToFlags(true, true, false, true); - var entries = new ArrayList(); - entries.add(new DeferredBatchTessellator.DrawRange(key, 0, 128, 4, GL11.GL_QUADS, flags)); - entries.add(new DeferredBatchTessellator.DrawRange(key, 128, 128, 4, GL11.GL_TRIANGLES, flags)); - entries.add(new DeferredBatchTessellator.DrawRange(key, 256, 128, 4, GL11.GL_QUADS, flags)); - - // Same state key, but different draw modes can't be merged. - // The flush logic subgroups by (drawMode, format flags). - // Count subgroups: QUADS(entry0), TRIANGLES(entry1), QUADS(entry2) - // Since entries aren't further sorted by drawMode, this produces 3 subgroups. - int subgroupCount = 0; - int i = 0; - while (i < entries.size()) { - int drawMode = entries.get(i).drawMode(); - while (i < entries.size() && entries.get(i).drawMode() == drawMode) i++; - subgroupCount++; - } - - // Two QUADS entries are not contiguous (TRIANGLES between them), so 3 subgroups - assertEquals(3, subgroupCount, "Different draw modes should form separate subgroups"); - } - @Test void stateKey_blendConstants() { // Verify all common GL blend constants roundtrip correctly @@ -188,29 +80,4 @@ class DeferredDrawBatcherTest { // Different tex enable states produce different keys assertNotEquals(key1, key2, "Different texture enable states should produce different keys"); } - - @Test - void drawRange_byteOffsetsNonOverlapping() { - long key = DeferredDrawBatcher.packStateKey(1, 0x0302, 0x0001, true, true, true, true); - final int flags = VertexFlags.convertToFlags(true, true, false, true); - - // Simulate sequential byte offsets as would be produced by interceptDraw - var ranges = new ArrayList(); - int offset = 0; - for (int i = 0; i < 5; i++) { - int byteLen = (i + 1) * 128; // 128, 256, 384, 512, 640 - ranges.add(new DeferredBatchTessellator.DrawRange(key, offset, byteLen, (i + 1) * 4, GL11.GL_QUADS, flags)); - offset += byteLen; - } - - // Verify ranges are contiguous and non-overlapping - for (int i = 1; i < ranges.size(); i++) { - var prev = ranges.get(i - 1); - var curr = ranges.get(i); - assertEquals(prev.byteOffset() + prev.byteLength(), curr.byteOffset(), "Range " + i + " should start where range " + (i - 1) + " ends"); - } - - // Total bytes = 128+256+384+512+640 = 1920 - assertEquals(1920, offset, "Total accumulated bytes"); - } } diff --git a/src/test/java/com/gtnewhorizons/angelica/commands/AngelicaCommandTest.java b/src/test/java/com/gtnewhorizons/angelica/commands/AngelicaCommandTest.java index dd904e913..edaefe4d9 100644 --- a/src/test/java/com/gtnewhorizons/angelica/commands/AngelicaCommandTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/commands/AngelicaCommandTest.java @@ -1,23 +1,22 @@ package com.gtnewhorizons.angelica.commands; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; class AngelicaCommandTest { - @Test - void worldAlteringViewsNeedCheats() { - assertTrue(AngelicaCommand.requiresCheats("wireframe")); - assertTrue(AngelicaCommand.requiresCheats("flyby")); - } - - @Test - void diagnosticsNeverNeedCheats() { - assertFalse(AngelicaCommand.requiresCheats("profile")); - assertFalse(AngelicaCommand.requiresCheats("fog")); - assertFalse(AngelicaCommand.requiresCheats("minimap")); - assertFalse(AngelicaCommand.requiresCheats("help")); + @ParameterizedTest(name = "{0} needs cheats: {1}") + @CsvSource({ + "wireframe, true", + "flyby, true", + "profile, false", + "fog, false", + "minimap, false", + "help, false", + }) + void onlyWorldAlteringViewsNeedCheats(String subcommand, boolean expected) { + assertEquals(expected, AngelicaCommand.requiresCheats(subcommand)); } } diff --git a/src/test/java/com/gtnewhorizons/angelica/config/GLVersionChoiceTest.java b/src/test/java/com/gtnewhorizons/angelica/config/GLVersionChoiceTest.java index b10e5fc11..f7a42bd53 100644 --- a/src/test/java/com/gtnewhorizons/angelica/config/GLVersionChoiceTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/config/GLVersionChoiceTest.java @@ -1,27 +1,23 @@ package com.gtnewhorizons.angelica.config; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; class GLVersionChoiceTest { - @Test - void zeroAndBelowTheFloorAreAuto() { - assertEquals(GLVersionChoice.AUTO, GLVersionChoice.of(0, 46)); - assertEquals(GLVersionChoice.AUTO, GLVersionChoice.of(32, 46)); - } - - @Test - void nonexistentVersionsRoundDown() { - assertEquals(GLVersionChoice.GL33, GLVersionChoice.of(34, 46)); - assertEquals(GLVersionChoice.GL33, GLVersionChoice.of(39, 46)); - assertEquals(GLVersionChoice.GL45, GLVersionChoice.of(45, 46)); - } - - @Test - void ceilingCaps() { - assertEquals(GLVersionChoice.GL41, GLVersionChoice.of(46, 41)); - assertEquals(GLVersionChoice.GL33, GLVersionChoice.of(41, 33)); + @ParameterizedTest(name = "{0}: of({1}, {2}) -> {3}") + @CsvSource({ + "zero is auto, 0, 46, AUTO", + "below the floor is auto, 32, 46, AUTO", + "nonexistent version rounds down, 34, 46, GL33", + "nonexistent version rounds down, 39, 46, GL33", + "existing version below the ceiling, 45, 46, GL45", + "ceiling caps, 46, 41, GL41", + "ceiling caps, 41, 33, GL33", + }) + void resolvesRequestedVersion(String name, int requested, int ceiling, GLVersionChoice expected) { + assertEquals(expected, GLVersionChoice.of(requested, ceiling)); } } diff --git a/src/test/java/com/gtnewhorizons/angelica/debug/flyby/FlybySceneTest.java b/src/test/java/com/gtnewhorizons/angelica/debug/flyby/FlybySceneTest.java index 37a1d2db4..f021f6f0c 100644 --- a/src/test/java/com/gtnewhorizons/angelica/debug/flyby/FlybySceneTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/debug/flyby/FlybySceneTest.java @@ -2,12 +2,16 @@ package com.gtnewhorizons.angelica.debug.flyby; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -26,41 +30,20 @@ class FlybySceneTest { return path.toString(); } - @Test - void dropsCommentsAndBlankLines() throws IOException { - final String path = writeFile("scene.txt", "# comment\n\ntime set day\n \nweather clear\n"); - assertArrayEquals(new String[] { "time set day", "weather clear" }, FlybyScene.load(path)); + static Stream sceneFiles() { + return Stream.of( + Arguments.of("drops comments and blank lines", "# comment\n\ntime set day\n \nweather clear\n", new String[] { "time set day", "weather clear" }), + Arguments.of("strips one leading slash", "//foo\n/bar\n", new String[] { "/foo", "bar" }), + Arguments.of("trims surrounding whitespace", " time set day \n", new String[] { "time set day" }), + Arguments.of("handles CRLF line endings", "time set day\r\nweather clear\r\n", new String[] { "time set day", "weather clear" }), + Arguments.of("drops a bare slash line", "time set day\n/\nweather clear\n", new String[] { "time set day", "weather clear" }), + Arguments.of("preserves order", "one\ntwo\nthree\n", new String[] { "one", "two", "three" })); } - @Test - void stripsOneLeadingSlash() throws IOException { - final String path = writeFile("scene.txt", "//foo\n/bar\n"); - assertArrayEquals(new String[] { "/foo", "bar" }, FlybyScene.load(path)); - } - - @Test - void trimsSurroundingWhitespace() throws IOException { - final String path = writeFile("scene.txt", " time set day \n"); - assertArrayEquals(new String[] { "time set day" }, FlybyScene.load(path)); - } - - @Test - void handlesCrlfLineEndings() throws IOException { - final Path path = tempDir.resolve("scene.txt"); - Files.write(path, "time set day\r\nweather clear\r\n".getBytes(StandardCharsets.UTF_8)); - assertArrayEquals(new String[] { "time set day", "weather clear" }, FlybyScene.load(path.toString())); - } - - @Test - void dropsBareSlashLine() throws IOException { - final String path = writeFile("scene.txt", "time set day\n/\nweather clear\n"); - assertArrayEquals(new String[] { "time set day", "weather clear" }, FlybyScene.load(path)); - } - - @Test - void preservesOrder() throws IOException { - final String path = writeFile("scene.txt", "one\ntwo\nthree\n"); - assertArrayEquals(new String[] { "one", "two", "three" }, FlybyScene.load(path)); + @ParameterizedTest(name = "{0}") + @MethodSource("sceneFiles") + void loadsCommands(String name, String content, String[] expected) throws IOException { + assertArrayEquals(expected, FlybyScene.load(writeFile("scene.txt", content))); } @Test diff --git a/src/test/java/com/gtnewhorizons/angelica/debug/profiling/AsprofRecorderTest.java b/src/test/java/com/gtnewhorizons/angelica/debug/profiling/AsprofRecorderTest.java index fdef62ecf..f1db146c0 100644 --- a/src/test/java/com/gtnewhorizons/angelica/debug/profiling/AsprofRecorderTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/debug/profiling/AsprofRecorderTest.java @@ -10,7 +10,12 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -21,19 +26,14 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class AsprofRecorderTest { - @Test - void commandWithDefaultOpts() { - assertEquals("start,jfr,event=wall,interval=5ms,alloc=512k,lock=10ms,file=/abs/x.jfr", AsprofRecorder.command("event=wall,interval=5ms,alloc=512k,lock=10ms", "/abs/x.jfr")); - } - - @Test - void commandWithCustomOpts() { - assertEquals("start,jfr,event=cpu,interval=1ms,file=/abs/y.jfr", AsprofRecorder.command("event=cpu,interval=1ms", "/abs/y.jfr")); - } - - @Test - void commandWithEmptyOpts() { - assertEquals("start,jfr,file=/abs/z.jfr", AsprofRecorder.command("", "/abs/z.jfr")); + @ParameterizedTest(name = "{0}") + @CsvSource({ + "default opts, 'event=wall,interval=5ms,alloc=512k,lock=10ms', /abs/x.jfr, 'start,jfr,event=wall,interval=5ms,alloc=512k,lock=10ms,file=/abs/x.jfr'", + "custom opts, 'event=cpu,interval=1ms', /abs/y.jfr, 'start,jfr,event=cpu,interval=1ms,file=/abs/y.jfr'", + "empty opts, '', /abs/z.jfr, 'start,jfr,file=/abs/z.jfr'", + }) + void buildsStartCommand(String name, String opts, String file, String expected) { + assertEquals(expected, AsprofRecorder.command(opts, file)); } @Test @@ -45,33 +45,20 @@ class AsprofRecorderTest { assertTrue(manual.matches("angelica-manual-\\d{8}-\\d{6}\\.jfr")); } - @Test - void fileNameIsDeterministicForSameMillis() { - final long fixedMillis = 1_700_000_000_000L; - assertEquals(AsprofRecorder.fileName("circuit", "jfr", fixedMillis), AsprofRecorder.fileName("circuit", "jfr", fixedMillis)); + static Stream firstLines() { + return Stream.of( + Arguments.of("null", null, ""), + Arguments.of("empty", "", ""), + Arguments.of("single line", "Profiler already started", "Profiler already started"), + Arguments.of("multi line", "Profiler already started\nat some.Stack\nat other.Stack", "Profiler already started")); } - @Test - void firstLineOnNull() { - assertEquals("", AsprofRecorder.firstLine(null)); + @ParameterizedTest(name = "{0}") + @MethodSource("firstLines") + void firstLine(String name, String input, String expected) { + assertEquals(expected, AsprofRecorder.firstLine(input)); } - @Test - void firstLineOnEmpty() { - assertEquals("", AsprofRecorder.firstLine("")); - } - - @Test - void firstLineOnSingleLine() { - assertEquals("Profiler already started", AsprofRecorder.firstLine("Profiler already started")); - } - - @Test - void firstLineOnMultiLine() { - assertEquals("Profiler already started", AsprofRecorder.firstLine("Profiler already started\nat some.Stack\nat other.Stack")); - } - - private static final class Commands implements Session.CommandExecutor { final List calls = new ArrayList<>(); Throwable failure; diff --git a/src/test/java/com/gtnewhorizons/angelica/iris/IrisDisplayListStateTest.java b/src/test/java/com/gtnewhorizons/angelica/iris/IrisDisplayListStateTest.java index 6024ffd1c..fa99e04ce 100644 --- a/src/test/java/com/gtnewhorizons/angelica/iris/IrisDisplayListStateTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/iris/IrisDisplayListStateTest.java @@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.iris; import com.gtnewhorizons.angelica.glsm.DisplayListManager; import com.gtnewhorizons.angelica.glsm.GLCoreTest; import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import com.gtnewhorizons.angelica.glsm.StateSet; import com.gtnewhorizons.angelica.glsm.recording.GLCommand; import com.gtnewhorizons.angelica.glsm.testutil.Reflect; @@ -63,29 +64,12 @@ class IrisDisplayListStateTest { } private int program() { - int vertex = shader(GL20.GL_VERTEX_SHADER, - "#version 330 core\nvoid main() { gl_Position = vec4(0, 0, 0, 1); }"); - int fragment = shader(GL20.GL_FRAGMENT_SHADER, + int program = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, "#version 330 core\nuniform int itemId; out vec4 color; void main() { color = vec4(float(itemId)); }"); - int program = GLStateManager.glCreateProgram(); programs.add(program); - GLStateManager.glAttachShader(program, vertex); - GLStateManager.glAttachShader(program, fragment); - GLStateManager.glLinkProgram(program); - GLStateManager.glDeleteShader(vertex); - GLStateManager.glDeleteShader(fragment); - assertEquals(GL11.GL_TRUE, GLStateManager.glGetProgrami(program, GL20.GL_LINK_STATUS)); return program; } - private static int shader(int type, String source) { - int shader = GLStateManager.glCreateShader(type); - GLStateManager.glShaderSource(shader, source); - GLStateManager.glCompileShader(shader); - assertEquals(GL11.GL_TRUE, GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS)); - return shader; - } - private void selectProgram() { IrisDisplayListState.runProgramTransition(() -> GLStateManager.glUseProgram(selectedProgram)); } diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/PacerCoreSimTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/PacerCoreSimTest.java index a7661e316..dedd88e32 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/PacerCoreSimTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/PacerCoreSimTest.java @@ -1,6 +1,5 @@ package com.gtnewhorizons.angelica.rendering; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import org.junit.jupiter.api.Test; import java.util.ArrayDeque; @@ -9,16 +8,20 @@ import java.util.Collections; import java.util.List; import java.util.Random; import java.util.function.LongConsumer; -import java.util.function.LongSupplier; import java.util.function.ObjIntConsumer; +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.MS; +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.P144; +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.P5994; +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.P60; +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.cadenceNanos; +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.driverPaced; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class PacerCoreSimTest { private static final long US = 1_000L; - private static final long MS = 1_000_000L; private static final int FRAMES = 3000; private static final int WARMUP = 300; @@ -27,10 +30,6 @@ class PacerCoreSimTest { private static final long SEED = 987654321L; private static final int MAILBOX_IMAGES = 2; - private static final long P60 = 1_000_000_000L / 60; - private static final long P5994 = 1_000_000_000L * 1001 / 60_000; - private static final long P144 = 1_000_000_000L / 144; - private enum Mode { FIFO, MAILBOX, IMMEDIATE } @@ -39,22 +38,13 @@ class PacerCoreSimTest { SWAP, FIRST_CLEAR, NONE } - private static final class FakeClock implements LongSupplier { - long now; - - @Override - public long getAsLong() { - return now; - } - } - private static final class FakeSleeper implements LongConsumer { - private final FakeClock clock; + private final PacerTestClock clock; private final Random rng = new Random(SEED); private final long granularity; int parks; - FakeSleeper(FakeClock clock, long granularity) { + FakeSleeper(PacerTestClock clock, long granularity) { this.clock = clock; this.granularity = granularity; } @@ -270,10 +260,10 @@ class PacerCoreSimTest { } private static Result run(Sim sim) { - final FakeClock clock = new FakeClock(); + final PacerTestClock clock = new PacerTestClock(); final FakeSleeper parker = new FakeSleeper(clock, sim.granularity); - final PacerSleeper sleeper = new PacerSleeper(clock, parker, false); - final PacerCore core = new PacerCore(clock, sleeper); + final PacerSleeper sleeper = new PacerSleeper(clock.clock(), parker, false); + final PacerCore core = new PacerCore(clock.clock(), sleeper); core.setRefreshPeriodNanos(sim.refreshPeriod); core.setTearFree(sim.tearFree); core.setVsyncOn(sim.vsyncOn); @@ -302,7 +292,7 @@ class PacerCoreSimTest { final int startParks = parker.parks - parksBefore; if (f >= WARMUP) result.startParks += startParks; result.probed[f] = core.probing(); - result.driven[f] = Reflect.invoke(core, "driverPaced", new Class[0]); + result.driven[f] = driverPaced(core); if (core.probing()) result.probeFrames++; if (core.locked()) { if (result.lockFrame < 0) result.lockFrame = f; @@ -314,7 +304,7 @@ class PacerCoreSimTest { wasLocked = false; } if (core.slackNanos() > result.maxStartSleep) result.maxStartSleep = core.slackNanos(); - result.cadences[f] = Reflect.get(core, "cadenceNanos"); + result.cadences[f] = cadenceNanos(core); result.margins[f] = core.marginNanos(); final long start = clock.now; @@ -342,8 +332,8 @@ class PacerCoreSimTest { clock.now = accepted; } - result.driverPaced = Reflect.invoke(core, "driverPaced", new Class[0]); - result.cadence = Reflect.get(core, "cadenceNanos"); + result.driverPaced = driverPaced(core); + result.cadence = cadenceNanos(core); result.margin = core.marginNanos(); display.resolve(result); measure(display.frames, result); diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/PacerCoreTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/PacerCoreTest.java index 68b51ce21..559312cae 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/PacerCoreTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/PacerCoreTest.java @@ -1,39 +1,40 @@ package com.gtnewhorizons.angelica.rendering; import com.gtnewhorizons.angelica.glsm.backend.RenderBackend; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.MS; +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.P144; +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.P5994; +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.P60; +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.cadenceNanos; +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.cpuHighNanos; +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.driverPaced; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class PacerCoreTest { - private static final long MS = 1_000_000L; - private static final long P60 = 1_000_000_000L / 60; - private static final long P5994 = 1_000_000_000L * 1001 / 60_000; - private static final long P144 = 1_000_000_000L / 144; - private static final class Rig { - final long[] now = { 0L }; + final PacerTestClock time = new PacerTestClock(); final PacerCore core; final List presents = new ArrayList<>(); - int parks; Rig(long refreshPeriodNanos, boolean tearFree, int capHz) { this(refreshPeriodNanos, tearFree, tearFree, capHz); } Rig(long refreshPeriodNanos, boolean tearFree, boolean vsyncOn, int capHz) { - final PacerSleeper sleeper = new PacerSleeper(() -> now[0], nanos -> { - parks++; - now[0] += nanos; - }, false); - core = new PacerCore(() -> now[0], sleeper); + final PacerSleeper sleeper = new PacerSleeper(time.clock(), time.parker(), false); + core = new PacerCore(time.clock(), sleeper); core.setRefreshPeriodNanos(refreshPeriodNanos); core.setTearFree(tearFree); core.setVsyncOn(vsyncOn); @@ -42,31 +43,31 @@ class PacerCoreTest { long frameWithFirstClear(long blockNanos, long workNanos) { if (blockNanos > 0L) { - core.onGateSample(blockNanos, now[0]); + core.onGateSample(blockNanos, time.now); } - now[0] = core.beginFrame(now[0]); - core.markFrameStart(now[0]); - now[0] += blockNanos + workNanos; - final long present = core.beforePresent(now[0], blockNanos); - now[0] = present; + time.now = core.beginFrame(time.now); + core.markFrameStart(time.now); + time.now += blockNanos + workNanos; + final long present = core.beforePresent(time.now, blockNanos); + time.now = present; presents.add(present); return present; } long frame(long workNanos) { - now[0] = core.beginFrame(now[0]); - core.markFrameStart(now[0]); - now[0] += workNanos; - final long present = core.beforePresent(now[0], 0L); - now[0] = present; + time.now = core.beginFrame(time.now); + core.markFrameStart(time.now); + time.now += workNanos; + final long present = core.beforePresent(time.now, 0L); + time.now = present; presents.add(present); return present; } void feed(long gateEndNanos, long durationNanos, long atNanos) { if (durationNanos > 0L) core.onGateSample(durationNanos, gateEndNanos); - now[0] = Math.max(now[0], atNanos); - now[0] = core.beginFrame(now[0]); + time.now = Math.max(time.now, atNanos); + time.now = core.beginFrame(time.now); } void feedRun(int count, long firstEndNanos, long durationNanos, long strideNanos) { @@ -79,18 +80,30 @@ class PacerCoreTest { void idleFrames(int count, long atNanos) { for (int i = 0; i < count; i++) { - now[0] = Math.max(now[0], atNanos + i); - core.beginFrame(now[0]); + time.now = Math.max(time.now, atNanos + i); + core.beginFrame(time.now); } } } - @Test - void aCapAtOrAboveTheRefreshRateCollapsesOntoTheRefreshPeriod() { - assertEquals(P5994, PacerCore.cadencePeriodNanos(60, P5994, true)); - assertEquals(P60, PacerCore.cadencePeriodNanos(58, P60, true)); - assertEquals(P60, PacerCore.cadencePeriodNanos(0, P60, true)); - assertEquals(P60, PacerCore.cadencePeriodNanos(240, P60, true)); + static Stream cadencePeriods() { + return Stream.of( + Arguments.of("a cap at the refresh rate collapses onto the refresh period", 60, P5994, true, P5994), + Arguments.of("a cap near the refresh rate collapses onto the refresh period", 58, P60, true, P60), + Arguments.of("no cap uses the refresh period when tear free", 0, P60, true, P60), + Arguments.of("a cap above the refresh rate collapses onto the refresh period", 240, P60, true, P60), + Arguments.of("a cap far from every multiple keeps its literal period", 45, P60, true, 1_000_000_000L / 45), + Arguments.of("a cap far from every multiple keeps its literal period", 100, P144, true, 1_000_000_000L / 100), + Arguments.of("a tearing mode ignores the refresh period", 100, P60, false, 10 * MS), + Arguments.of("a tearing mode with no cap is unpaced", 0, P60, false, 0L), + Arguments.of("a zero refresh period with no cap is unpaced", 0, 0L, true, 0L), + Arguments.of("a zero refresh period keeps the literal cap", 100, 0L, true, 10 * MS)); + } + + @ParameterizedTest(name = "{0}: cadencePeriodNanos({1}, {2}, {3}) -> {4}") + @MethodSource("cadencePeriods") + void cadencePeriodNanos(String name, int capHz, long refreshPeriodNanos, boolean tearFree, long expected) { + assertEquals(expected, PacerCore.cadencePeriodNanos(capHz, refreshPeriodNanos, tearFree)); } @Test @@ -100,26 +113,6 @@ class PacerCoreTest { assertEquals(48, RenderBackend.hzFromPeriod(3 * P144)); } - @Test - void aCapFarFromEveryMultipleKeepsItsLiteralPeriod() { - assertEquals(1_000_000_000L / 45, PacerCore.cadencePeriodNanos(45, P60, true)); - assertEquals(1_000_000_000L / 100, PacerCore.cadencePeriodNanos(100, P144, true)); - } - - @Test - void aTearingModeIgnoresTheRefreshPeriodEntirely() { - assertEquals(10 * MS, PacerCore.cadencePeriodNanos(100, P60, false)); - assertEquals(0L, PacerCore.cadencePeriodNanos(0, P60, false)); - assertEquals(0L, PacerCore.cadencePeriodNanos(0, 0L, true)); - assertEquals(10 * MS, PacerCore.cadencePeriodNanos(100, 0L, true)); - } - - @Test - void aZeroCapUsesTheRefreshPeriodOnlyWhenTearFree() { - assertEquals(P60, PacerCore.cadencePeriodNanos(0, P60, true)); - assertEquals(0L, PacerCore.cadencePeriodNanos(0, P60, false)); - } - @Test void threeConsistentPairsOnTheRefreshGridLockThePhase() { final Rig rig = new Rig(P60, true, 0); @@ -129,7 +122,7 @@ class PacerCoreTest { rig.feed(20 * MS + PacerCore.LOCK_PAIRS * P60, 5 * MS, 20 * MS + PacerCore.LOCK_PAIRS * P60); assertTrue(rig.core.locked()); - assertTrue(Reflect.invoke(rig.core, "driverPaced", new Class[0])); + assertTrue(driverPaced(rig.core)); assertFalse(rig.core.probing()); } @@ -254,8 +247,8 @@ class PacerCoreTest { final Rig rig = new Rig(P60, true, 30); rig.feedRun(PacerCore.LOCK_PAIRS + 1, 20 * MS, 5 * MS, P60); assertTrue(rig.core.locked()); - assertEquals(2 * P60, Reflect.get(rig.core, "cadenceNanos")); - assertFalse(Reflect.invoke(rig.core, "driverPaced", new Class[0])); + assertEquals(2 * P60, cadenceNanos(rig.core)); + assertFalse(driverPaced(rig.core)); } @Test @@ -272,13 +265,13 @@ class PacerCoreTest { blocked.frameWithFirstClear(5 * MS, 7 * MS); blocked.frame(4 * MS); - assertEquals(7 * MS, Reflect.get(blocked.core, "cpuHighNanos")); + assertEquals(7 * MS, cpuHighNanos(blocked.core)); final Rig plain = new Rig(P60, false, 60); plain.frame(12 * MS); plain.frame(4 * MS); - assertEquals(12 * MS, Reflect.get(plain.core, "cpuHighNanos")); + assertEquals(12 * MS, cpuHighNanos(plain.core)); } @Test @@ -304,17 +297,17 @@ class PacerCoreTest { final Rig rig = new Rig(P60, false, 60); for (int i = 0; i < 200; i++) rig.frame(4 * MS); final long before = rig.core.marginNanos(); - final long cpuBefore = Reflect.get(rig.core, "cpuHighNanos"); + final long cpuBefore = cpuHighNanos(rig.core); assertEquals(4 * MS, cpuBefore); rig.frame(208 * MS); rig.frame(4 * MS); assertEquals(before, rig.core.marginNanos()); - assertEquals(cpuBefore, Reflect.get(rig.core, "cpuHighNanos")); + assertEquals(cpuBefore, cpuHighNanos(rig.core)); rig.frame(4 * MS); assertEquals(before, rig.core.marginNanos()); - assertEquals(cpuBefore, Reflect.get(rig.core, "cpuHighNanos")); + assertEquals(cpuBefore, cpuHighNanos(rig.core)); } @Test @@ -338,33 +331,33 @@ class PacerCoreTest { void idleWorkStillRunsWhenTheDriverPaces() { final Rig rig = new Rig(P60, true, 0); rig.feedRun(PacerCore.LOCK_PAIRS + 1, 20 * MS, 5 * MS, P60); - assertTrue(Reflect.invoke(rig.core, "driverPaced", new Class[0])); + assertTrue(driverPaced(rig.core)); final int[] runs = { 0 }; rig.core.setIdleWork(deadline -> { runs[0]++; - rig.now[0] += MS; + rig.time.now += MS; }); - final int parksBefore = rig.parks; + final int parksBefore = rig.time.parks; for (int i = 0; i < 10; i++) rig.frame(2 * MS); assertEquals(10, runs[0], "a driver-paced frame still has idle time before the present target"); - assertEquals(parksBefore, rig.parks, "but it must never sleep"); + assertEquals(parksBefore, rig.time.parks, "but it must never sleep"); } @Test void theFirstPacedFrameAfterAnUnpacedStretchIsAnchoredWithoutSleeping() { final Rig rig = new Rig(P60, false, 0); for (int i = 0; i < 5; i++) rig.frame(4 * MS); - assertEquals(0, rig.parks); + assertEquals(0, rig.time.parks); rig.core.setCap(60); rig.frame(4 * MS); - assertEquals(0, rig.parks, "the anchor frame must not stall the loop"); + assertEquals(0, rig.time.parks, "the anchor frame must not stall the loop"); assertEquals(0L, rig.core.slackNanos()); rig.frame(4 * MS); - assertTrue(rig.parks > 0, "the cadence resumes on the frame after the anchor"); + assertTrue(rig.time.parks > 0, "the cadence resumes on the frame after the anchor"); } @Test @@ -372,8 +365,8 @@ class PacerCoreTest { final Rig rig = new Rig(P60, false, 60); for (int i = 0; i < 20; i++) rig.frame(4 * MS); - rig.now[0] -= 500 * MS; - rig.core.beginFrame(rig.now[0]); + rig.time.now -= 500 * MS; + rig.core.beginFrame(rig.time.now); assertTrue(rig.core.slackNanos() <= P60, "a backward clock step must not park for half a second: " + rig.core.slackNanos()); } @@ -395,7 +388,7 @@ class PacerCoreTest { void anUnpacedFrameNeverSleeps() { final Rig rig = new Rig(P60, false, 0); for (int i = 0; i < 50; i++) rig.frame(4 * MS); - assertEquals(0, rig.parks); + assertEquals(0, rig.time.parks); assertEquals(0L, rig.core.slackNanos()); } @@ -438,7 +431,7 @@ class PacerCoreTest { assertFalse(rig.core.probing()); } assertFalse(rig.core.locked()); - assertEquals(P60, Reflect.get(rig.core, "cadenceNanos")); + assertEquals(P60, cadenceNanos(rig.core)); } @Test @@ -476,7 +469,7 @@ class PacerCoreTest { blocked.frameWithFirstClear(5 * MS, 4 * MS); } - assertEquals(Reflect.get(plain.core, "cpuHighNanos"), Reflect.get(blocked.core, "cpuHighNanos")); + assertEquals(cpuHighNanos(plain.core), cpuHighNanos(blocked.core)); assertEquals(plain.core.marginNanos(), blocked.core.marginNanos()); assertTrue(blocked.core.marginNanos() < PacerCore.MARGIN_CEILING_NANOS, "the driver block must not saturate the margin"); } diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/PacerSleeperTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/PacerSleeperTest.java index 7d35b233a..fdf3a0fc7 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/PacerSleeperTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/PacerSleeperTest.java @@ -3,52 +3,21 @@ package com.gtnewhorizons.angelica.rendering; import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import org.junit.jupiter.api.Test; -import java.util.function.LongConsumer; -import java.util.function.LongSupplier; - +import static com.gtnewhorizons.angelica.rendering.PacerTestClock.MS; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class PacerSleeperTest { - private static final long MS = 1_000_000L; private static final long COLD_FLOOR = PacerSleeper.MIN_SPIN_FLOOR_NANOS; - private static final class FakeTime { - long now; - long lag; - long granularity; - long nextParkActual = -1; - int parks; - long lastRequest; - - LongSupplier clock() { - return () -> now; - } - - LongConsumer parker() { - return nanos -> { - parks++; - lastRequest = nanos; - final long actual; - if (nextParkActual >= 0) { - actual = nextParkActual; - nextParkActual = -1; - } else { - actual = Math.max(nanos + lag, granularity); - } - now += actual; - }; - } - } - - private static PacerSleeper sleeper(FakeTime time) { + private static PacerSleeper sleeper(PacerTestClock time) { return new PacerSleeper(time.clock(), time.parker(), false); } @Test void aLongWaitCostsOneParkNotAChunkPerMillisecond() { - final FakeTime time = new FakeTime(); + final PacerTestClock time = new PacerTestClock(); final PacerSleeper s = sleeper(time); s.sleepUntil(10 * MS, 0L); @@ -59,7 +28,7 @@ class PacerSleeperTest { @Test void aWellBehavedTimerLeavesNoMoreThanTheFloorToSpin() { - final FakeTime time = new FakeTime(); + final PacerTestClock time = new PacerTestClock(); time.lag = 20_000L; final PacerSleeper s = sleeper(time); @@ -73,7 +42,7 @@ class PacerSleeperTest { @Test void theLagEstimateConvergesSoTheDeadlineIsNotOvershot() { - final FakeTime time = new FakeTime(); + final PacerTestClock time = new PacerTestClock(); time.lag = 300_000L; final PacerSleeper s = sleeper(time); @@ -89,7 +58,7 @@ class PacerSleeperTest { @Test void anEarlyWakeReParksInsteadOfSpinningTheRemainder() { - final FakeTime time = new FakeTime(); + final PacerTestClock time = new PacerTestClock(); final PacerSleeper s = sleeper(time); time.nextParkActual = 2 * MS; @@ -101,7 +70,7 @@ class PacerSleeperTest { @Test void aCoarseTimerParksLateRatherThanSpinningOutTheFrame() { - final FakeTime time = new FakeTime(); + final PacerTestClock time = new PacerTestClock(); time.granularity = 15_600_000L; final PacerSleeper s = sleeper(time); @@ -126,7 +95,7 @@ class PacerSleeperTest { @Test void aOneOffStallDoesNotPoisonTheEstimate() { - final FakeTime time = new FakeTime(); + final PacerTestClock time = new PacerTestClock(); final PacerSleeper s = sleeper(time); time.nextParkActual = 500 * MS; @@ -137,7 +106,7 @@ class PacerSleeperTest { @Test void eachParkEvictsExactlyOneSlotFromTheEstimate() { - final FakeTime time = new FakeTime(); + final PacerTestClock time = new PacerTestClock(); time.lag = MS; final PacerSleeper s = sleeper(time); @@ -153,7 +122,7 @@ class PacerSleeperTest { @Test void interruptionAbandonsTheWait() { - final FakeTime time = new FakeTime(); + final PacerTestClock time = new PacerTestClock(); final PacerSleeper s = sleeper(time); Thread.currentThread().interrupt(); @@ -212,7 +181,7 @@ class PacerSleeperTest { @Test void aSteadyPlatformNeedsOnlyTheMinimumSpinFloor() { - final FakeTime time = new FakeTime(); + final PacerTestClock time = new PacerTestClock(); time.lag = 300_000L; final PacerSleeper s = sleeper(time); @@ -225,7 +194,7 @@ class PacerSleeperTest { @Test void aJitteryPlatformWidensTheSpinFloorToCoverTheWorstPark() { - final FakeTime time = new FakeTime(); + final PacerTestClock time = new PacerTestClock(); time.lag = 100_000L; final PacerSleeper s = sleeper(time); @@ -241,7 +210,7 @@ class PacerSleeperTest { @Test void aDeadlineInsideTheFloorParksNotAtAll() { - final FakeTime time = new FakeTime(); + final PacerTestClock time = new PacerTestClock(); final PacerSleeper s = sleeper(time); final long woke = s.sleepUntil(COLD_FLOOR, 0L); @@ -252,7 +221,7 @@ class PacerSleeperTest { @Test void everyWaitInAFrameLandsOnTheOneFrameOfBookkeeping() { - final FakeTime time = new FakeTime(); + final PacerTestClock time = new PacerTestClock(); final PacerSleeper s = sleeper(time); s.sleepUntil(10 * MS, 0L); @@ -275,7 +244,7 @@ class PacerSleeperTest { @Test void aWakeInsideTheToleranceIsNotLate() { - final FakeTime time = new FakeTime(); + final PacerTestClock time = new PacerTestClock(); final PacerSleeper s = sleeper(time); s.noteFrame(0L, PacerSleeper.LATE_TOL_NANOS); diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/PacerTestClock.java b/src/test/java/com/gtnewhorizons/angelica/rendering/PacerTestClock.java new file mode 100644 index 000000000..f13b06f3e --- /dev/null +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/PacerTestClock.java @@ -0,0 +1,54 @@ +package com.gtnewhorizons.angelica.rendering; + +import com.gtnewhorizons.angelica.glsm.testutil.Reflect; + +import java.util.function.LongConsumer; +import java.util.function.LongSupplier; + +/** Fake clock plus a parker that advances it; with the defaults a park takes exactly the requested time. */ +final class PacerTestClock { + + static final long MS = 1_000_000L; + static final long P60 = 1_000_000_000L / 60; + static final long P5994 = 1_000_000_000L * 1001 / 60_000; + static final long P144 = 1_000_000_000L / 144; + + long now; + long lag; + long granularity; + /** One-shot override of the next park's real duration; negative means unset. */ + long nextParkActual = -1; + int parks; + long lastRequest; + + LongSupplier clock() { + return () -> now; + } + + LongConsumer parker() { + return nanos -> { + parks++; + lastRequest = nanos; + final long actual; + if (nextParkActual >= 0) { + actual = nextParkActual; + nextParkActual = -1; + } else { + actual = Math.max(nanos + lag, granularity); + } + now += actual; + }; + } + + static boolean driverPaced(PacerCore core) { + return Reflect.invoke(core, "driverPaced", new Class[0]); + } + + static long cadenceNanos(PacerCore core) { + return Reflect.get(core, "cadenceNanos"); + } + + static long cpuHighNanos(PacerCore core) { + return Reflect.get(core, "cpuHighNanos"); + } +} diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/PlayerReflectionLayoutTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/PlayerReflectionLayoutTest.java index 706036efe..676879ef2 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/PlayerReflectionLayoutTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/PlayerReflectionLayoutTest.java @@ -1,13 +1,12 @@ package com.gtnewhorizons.angelica.rendering; import net.minecraft.client.model.ModelBiped; -import net.minecraft.client.model.ModelBox; import net.minecraft.client.model.ModelRenderer; -import net.minecraft.client.model.TexturedQuad; import org.junit.jupiter.api.Test; -import java.lang.reflect.Field; - +import static com.gtnewhorizons.angelica.rendering.PlayerReflectionTestSupport.FLOATS; +import static com.gtnewhorizons.angelica.rendering.PlayerReflectionTestSupport.MODEL_SCALE; +import static com.gtnewhorizons.angelica.rendering.PlayerReflectionTestSupport.emit; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; @@ -15,35 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class PlayerReflectionLayoutTest { - static final int FLOATS = PlayerReflectionCapture.FLOATS_PER_VERTEX; - static final int PART_VERTS = PlayerReflectionCapture.VERTICES_PER_PART; - static final float MODEL_SCALE = 0.0625f; - - private static Field quadListField; - - static TexturedQuad[] quadsOf(ModelRenderer part) { - if (part == null || part.cubeList == null || part.cubeList.isEmpty()) return null; - try { - if (quadListField == null) { - quadListField = ModelBox.class.getDeclaredField("quadList"); - quadListField.setAccessible(true); - } - return (TexturedQuad[]) quadListField.get(part.cubeList.getFirst()); - } catch (ReflectiveOperationException e) { - throw new AssertionError("ModelBox.quadList is no longer reachable", e); - } - } - - static float[] emit(ModelBiped model, float scale) { - final float[] out = new float[PlayerReflectionCapture.VERTEX_COUNT * FLOATS]; - final ModelRenderer[] parts = PlayerReflectionCapture.partOrder(model, new ModelRenderer[PlayerReflectionCapture.PART_COUNT]); - int o = 0; - for (ModelRenderer part : parts) { - o = PlayerReflectionCapture.emitBox(part, quadsOf(part), out, o, scale); - } - assertEquals(out.length, o, "emit did not fill exactly 288 vertices"); - return out; - } + private static final int PART_VERTS = PlayerReflectionCapture.VERTICES_PER_PART; private static float min(float[] v, int block, int component) { float m = Float.POSITIVE_INFINITY; diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/PlayerReflectionSsboMappingTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/PlayerReflectionSsboMappingTest.java index 8edbeba43..6ec5e85c3 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/PlayerReflectionSsboMappingTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/PlayerReflectionSsboMappingTest.java @@ -19,8 +19,8 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; -import static com.gtnewhorizons.angelica.rendering.PlayerReflectionLayoutTest.MODEL_SCALE; -import static com.gtnewhorizons.angelica.rendering.PlayerReflectionLayoutTest.emit; +import static com.gtnewhorizons.angelica.rendering.PlayerReflectionTestSupport.MODEL_SCALE; +import static com.gtnewhorizons.angelica.rendering.PlayerReflectionTestSupport.emit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -108,7 +108,7 @@ class PlayerReflectionSsboMappingTest { GLStateManager.glBindVertexArray(vao); GLStateManager.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); GLStateManager.glBufferData(GL15.GL_ARRAY_BUFFER, verts, GL15.GL_STATIC_DRAW); - final int stride = PlayerReflectionLayoutTest.FLOATS * 4; + final int stride = PlayerReflectionTestSupport.FLOATS * 4; GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, stride, 0L); GL20.glEnableVertexAttribArray(0); GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, stride, 12L); @@ -138,8 +138,7 @@ class PlayerReflectionSsboMappingTest { return out; } - @Test - void quadIndexPatternProducesTheVertexIdsThePackIndexesBy() { + private float[] drawBipedIntoSsbos() { assumeTrue(RenderSystem.supportsSSBO(), "SSBOs unsupported"); final float[] xyzuv = emit(new ModelBiped(0.0f), MODEL_SCALE); @@ -150,9 +149,15 @@ class PlayerReflectionSsboMappingTest { GLStateManager.glUseProgram(program); GLStateManager.glBindVertexArray(vao); QuadConverter.drawQuadsAsTriangles(GLStateManager.ctx(), 0, PlayerReflectionCapture.VERTEX_COUNT); + return xyzuv; + } + + @Test + void quadIndexPatternProducesTheVertexIdsThePackIndexesBy() { + final float[] xyzuv = drawBipedIntoSsbos(); final ByteBuffer ssboData = readback(ssbo, SSBO_BYTES); - final int floats = PlayerReflectionLayoutTest.FLOATS; + final int floats = PlayerReflectionTestSupport.FLOATS; for (int i = 0; i < SSBO_ENTRIES; i++) { final int vertexId = i + i / 3; @@ -167,16 +172,7 @@ class PlayerReflectionSsboMappingTest { @Test void everyQuadCornerRunsTheVertexShader() { - assumeTrue(RenderSystem.supportsSSBO(), "SSBOs unsupported"); - - final float[] xyzuv = emit(new ModelBiped(0.0f), MODEL_SCALE); - buildProgram(); - uploadVertices(xyzuv); - allocateSsbo(); - - GLStateManager.glUseProgram(program); - GLStateManager.glBindVertexArray(vao); - QuadConverter.drawQuadsAsTriangles(GLStateManager.ctx(), 0, PlayerReflectionCapture.VERTEX_COUNT); + drawBipedIntoSsbos(); final ByteBuffer visitedData = readback(visitedSsbo, VISITED_BYTES); for (int vertexId = 0; vertexId < PlayerReflectionCapture.VERTEX_COUNT; vertexId++) { diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/PlayerReflectionTestSupport.java b/src/test/java/com/gtnewhorizons/angelica/rendering/PlayerReflectionTestSupport.java new file mode 100644 index 000000000..fb40a9704 --- /dev/null +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/PlayerReflectionTestSupport.java @@ -0,0 +1,32 @@ +package com.gtnewhorizons.angelica.rendering; + +import com.gtnewhorizons.angelica.glsm.testutil.Reflect; +import net.minecraft.client.model.ModelBiped; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.client.model.TexturedQuad; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +final class PlayerReflectionTestSupport { + + static final int FLOATS = PlayerReflectionCapture.FLOATS_PER_VERTEX; + static final float MODEL_SCALE = 0.0625f; + + private PlayerReflectionTestSupport() {} + + static TexturedQuad[] quadsOf(ModelRenderer part) { + if (part == null || part.cubeList == null || part.cubeList.isEmpty()) return null; + return Reflect.get(part.cubeList.getFirst(), "quadList"); + } + + static float[] emit(ModelBiped model, float scale) { + final float[] out = new float[PlayerReflectionCapture.VERTEX_COUNT * FLOATS]; + final ModelRenderer[] parts = PlayerReflectionCapture.partOrder(model, new ModelRenderer[PlayerReflectionCapture.PART_COUNT]); + int o = 0; + for (ModelRenderer part : parts) { + o = PlayerReflectionCapture.emitBox(part, quadsOf(part), out, o, scale); + } + assertEquals(out.length, o, "emit did not fill exactly 288 vertices"); + return out; + } +} diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/ReducerStateMachineTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/ReducerStateMachineTest.java index ff26bb68b..64460a72b 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/ReducerStateMachineTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/ReducerStateMachineTest.java @@ -4,6 +4,12 @@ import com.gtnewhorizons.angelica.config.SystemProperties.FlybyPacing; import com.gtnewhorizons.angelica.rendering.ReducerStateMachine.State; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -122,42 +128,35 @@ class ReducerStateMachineTest { } - @Test - void unlimitedUserAndNoStateCapIsUncapped() { - assertEquals(0, ReducerStateMachine.mergeCap(0, 0, false, true)); - assertEquals(0, ReducerStateMachine.mergeCap(260, 0, false, true)); + @ParameterizedTest(name = "{0}: mergeCap({1}, {2}, {3}, {4}) -> {5}") + @CsvSource({ + "unlimited user and no state cap is uncapped, 0, 0, false, true, 0", + "unlimited user and no state cap is uncapped, 260, 0, false, true, 0", + "the lower of the user and state cap wins, 120, 10, false, true, 10", + "the lower of the user and state cap wins, 30, 60, false, true, 30", + "menu cap applies only when the option is on, 0, 0, true, true, 30", + "menu cap applies only when the option is on, 0, 0, true, false, 0", + "menu cap never raises the user cap, 20, 0, true, true, 20", + "menu cap never raises the user cap, 260, 0, true, true, 30", + "menu cap never raises the user cap, 260, 10, true, true, 10", + }) + void mergeCap(String name, int userCap, int stateCap, boolean inMenu, boolean menuCapOption, int expected) { + assertEquals(expected, ReducerStateMachine.mergeCap(userCap, stateCap, inMenu, menuCapOption)); } - @Test - void benchmarkCapIsZeroWhenUncapped() { - assertEquals(0, ReducerStateMachine.benchmarkCap(FlybyPacing.UNCAPPED, 60)); - assertEquals(0, ReducerStateMachine.benchmarkCap(FlybyPacing.UNCAPPED, 0)); + static Stream benchmarkCaps() { + return Stream.of( + Arguments.of("uncapped ignores the configured cap", FlybyPacing.UNCAPPED, 60, 0), + Arguments.of("uncapped ignores the configured cap", FlybyPacing.UNCAPPED, 0, 0), + Arguments.of("configured cap is used as is", FlybyPacing.CONFIGURED, 60, 60), + Arguments.of("configured zero is uncapped", FlybyPacing.CONFIGURED, 0, 0), + Arguments.of("configured max setting is uncapped", FlybyPacing.CONFIGURED, ReducerStateMachine.MAX_FPS_SETTING, 0)); } - @Test - void configuredBenchmarkCapIgnoresReducerAndMenu() { - assertEquals(60, ReducerStateMachine.benchmarkCap(FlybyPacing.CONFIGURED, 60)); - assertEquals(0, ReducerStateMachine.benchmarkCap(FlybyPacing.CONFIGURED, 0)); - assertEquals(0, ReducerStateMachine.benchmarkCap(FlybyPacing.CONFIGURED, ReducerStateMachine.MAX_FPS_SETTING)); - } - - @Test - void theLowerOfTheUserAndStateCapWins() { - assertEquals(10, ReducerStateMachine.mergeCap(120, 10, false, true)); - assertEquals(30, ReducerStateMachine.mergeCap(30, 60, false, true)); - } - - @Test - void menuCapAppliesOnlyWhenOptionOn() { - assertEquals(30, ReducerStateMachine.mergeCap(0, 0, true, true)); - assertEquals(0, ReducerStateMachine.mergeCap(0, 0, true, false)); - } - - @Test - void menuCapNeverRaisesUserCap() { - assertEquals(20, ReducerStateMachine.mergeCap(20, 0, true, true)); - assertEquals(30, ReducerStateMachine.mergeCap(260, 0, true, true)); - assertEquals(10, ReducerStateMachine.mergeCap(260, 10, true, true)); + @ParameterizedTest(name = "{0}: benchmarkCap({1}, {2}) -> {3}") + @MethodSource("benchmarkCaps") + void benchmarkCap(String name, FlybyPacing pacing, int configured, int expected) { + assertEquals(expected, ReducerStateMachine.benchmarkCap(pacing, configured)); } @Test diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/VertexAttributeLayoutTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/VertexAttributeLayoutTest.java index 7b6618c1b..489af75b2 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/VertexAttributeLayoutTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/VertexAttributeLayoutTest.java @@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; -import java.util.TreeSet; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -65,14 +64,4 @@ class VertexAttributeLayoutTest { assertEquals(InstancedAttribs.LOC_ROW0 + 1, InstancedAttribs.LOC_ROW1, "instance row 1 must sit at LOC_ROW0 + 1"); assertEquals(InstancedAttribs.LOC_ROW0 + 2, InstancedAttribs.LOC_ROW2, "instance row 2 must sit at LOC_ROW0 + 2"); } - - @Test - void reportsRemainingFreeLocations() { - reservedAttributeLocationsDoNotOverlap(); - final TreeSet free = new TreeSet<>(); - for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++) { - if (!claimed.containsKey(i)) free.add(i); - } - System.out.println("free vertex attribute locations: " + free); - } } diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/CameraTransformReuseTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/CameraTransformReuseTest.java index 94729fa4d..147f7eac0 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/CameraTransformReuseTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/CameraTransformReuseTest.java @@ -1,38 +1,34 @@ package com.gtnewhorizons.angelica.rendering.celeritas; +import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import org.embeddedt.embeddium.impl.render.viewport.CameraTransform; import org.junit.jupiter.api.Test; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; class CameraTransformReuseTest { + + private static CameraTransform lookup(CeleritasWorldRenderer renderer, double x, double y, double z) { + return Reflect.invoke(renderer, "cameraTransform", new Class[] { double.class, double.class, double.class }, x, y, z); + } + @Test - void identicalCoordinatesReuseImmutableUpstreamTransform() throws ReflectiveOperationException { - final Class unsafeClass = Class.forName("sun.misc.Unsafe"); - final Field singleton = unsafeClass.getDeclaredField("theUnsafe"); - singleton.setAccessible(true); - final Object unsafe = singleton.get(null); - final CeleritasWorldRenderer renderer = (CeleritasWorldRenderer) unsafeClass.getMethod("allocateInstance", Class.class) - .invoke(unsafe, CeleritasWorldRenderer.class); - final Method lookup = CeleritasWorldRenderer.class.getDeclaredMethod("cameraTransform", double.class, double.class, double.class); - lookup.setAccessible(true); - final CameraTransform original = (CameraTransform) lookup.invoke(renderer, 1475.7192986248613, 65.625, -935.8161276616005); - assertSame(original, lookup.invoke(renderer, original.x, original.y, original.z)); - final CameraTransform movedX = (CameraTransform) lookup.invoke(renderer, original.x + 0.125, original.y, original.z); - final CameraTransform movedY = (CameraTransform) lookup.invoke(renderer, movedX.x, movedX.y + 0.125, movedX.z); - final CameraTransform movedZ = (CameraTransform) lookup.invoke(renderer, movedY.x, movedY.y, movedY.z + 0.125); + void identicalCoordinatesReuseImmutableUpstreamTransform() { + final CeleritasWorldRenderer renderer = Reflect.allocate(CeleritasWorldRenderer.class); + final CameraTransform original = lookup(renderer, 1475.7192986248613, 65.625, -935.8161276616005); + assertSame(original, lookup(renderer, original.x, original.y, original.z)); + final CameraTransform movedX = lookup(renderer, original.x + 0.125, original.y, original.z); + final CameraTransform movedY = lookup(renderer, movedX.x, movedX.y + 0.125, movedX.z); + final CameraTransform movedZ = lookup(renderer, movedY.x, movedY.y, movedY.z + 0.125); assertNotSame(original, movedX); assertNotSame(movedX, movedY); assertNotSame(movedY, movedZ); assertEquals(1475.7192986248613, original.x); assertEquals(65.625, original.y); assertEquals(-935.8161276616005, original.z); - assertSame(movedZ, lookup.invoke(renderer, movedZ.x, movedZ.y, movedZ.z)); + assertSame(movedZ, lookup(renderer, movedZ.x, movedZ.y, movedZ.z)); final CameraTransform expected = new CameraTransform(movedZ.x, movedZ.y, movedZ.z); assertEquals(expected.intX, movedZ.intX); assertEquals(expected.intY, movedZ.intY); diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/DeferredSectionMeshTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/DeferredSectionMeshTest.java index da91a0bbe..76aede482 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/DeferredSectionMeshTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/DeferredSectionMeshTest.java @@ -4,10 +4,14 @@ import com.gtnewhorizons.angelica.rendering.celeritas.threading.ThreadedAngelica import org.embeddedt.embeddium.impl.render.chunk.RenderSection; import org.embeddedt.embeddium.impl.util.task.CancellationToken; import org.joml.Vector3d; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; class DeferredSectionMeshTest { @@ -25,36 +29,20 @@ class DeferredSectionMeshTest { return new DeferredSectionMesh(task, token, null, null, null, null, null, 0L, false, 0L); } - @Test - void freshSectionIsNotObsolete() { - final RenderSection section = new RenderSection(null, 0, 0, 0); - assertFalse(mesh(section, 5, false).isObsolete()); + static Stream sections() { + return Stream.of( + Arguments.of("fresh section", (Consumer) section -> {}, false, false), + Arguments.of("same built frame", (Consumer) section -> section.setLastBuiltFrame(5), false, false), + Arguments.of("newer built frame", (Consumer) section -> section.setLastBuiltFrame(6), false, true), + Arguments.of("cancelled token", (Consumer) section -> {}, true, true), + Arguments.of("deleted section", (Consumer) RenderSection::delete, false, true)); } - @Test - void sameBuiltFrameIsNotObsolete() { + @ParameterizedTest(name = "{0} -> obsolete={3}") + @MethodSource("sections") + void isObsolete(String name, Consumer setup, boolean cancelled, boolean expected) { final RenderSection section = new RenderSection(null, 0, 0, 0); - section.setLastBuiltFrame(5); - assertFalse(mesh(section, 5, false).isObsolete()); - } - - @Test - void newerBuiltFrameIsObsolete() { - final RenderSection section = new RenderSection(null, 0, 0, 0); - section.setLastBuiltFrame(6); - assertTrue(mesh(section, 5, false).isObsolete()); - } - - @Test - void cancelledTokenIsObsolete() { - final RenderSection section = new RenderSection(null, 0, 0, 0); - assertTrue(mesh(section, 5, true).isObsolete()); - } - - @Test - void deletedSectionIsObsolete() { - final RenderSection section = new RenderSection(null, 0, 0, 0); - section.delete(); - assertTrue(mesh(section, 5, false).isObsolete()); + setup.accept(section); + assertEquals(expected, mesh(section, 5, cancelled).isObsolete()); } } diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/SectionFrustumGateTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/SectionFrustumGateTest.java index 95cff85e4..8860376dd 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/SectionFrustumGateTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/SectionFrustumGateTest.java @@ -15,20 +15,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class SectionFrustumGateTest { - private static FrustumIntersection knownFrustum() { - return new FrustumIntersection(new Matrix4f().perspective((float) Math.toRadians(90), 1.0f, 0.1f, 100.0f)); - } - - @Test - void triStateVerdictsAgainstKnownPerspective() { - final FrustumIntersection frustum = knownFrustum(); - - assertEquals(FrustumIntersection.INSIDE, frustum.intersectAab(-1, -1, -50, 1, 1, -40)); - assertEquals(FrustumIntersection.INTERSECT, frustum.intersectAab(-1, -1, -5, 1, 1, 5)); - assertTrue(frustum.intersectAab(-1, -1, 10, 1, 1, 20) >= 0); - assertTrue(frustum.intersectAab(-100, -1, -20, -50, 1, -10) >= 0); - } - @Test void sectionVerdictIsSoundForContainedBoxes() { final Random rng = new Random(0x5EC7104); diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/TerrainShaderLinkTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/TerrainShaderLinkTest.java index d24503f6b..d4336c329 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/TerrainShaderLinkTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/TerrainShaderLinkTest.java @@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.rendering.celeritas; import com.gtnewhorizons.angelica.glsm.GLCoreTest; import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import org.embeddedt.embeddium.impl.gl.attribute.GlVertexAttribute; import org.embeddedt.embeddium.impl.gl.shader.ShaderConstants; import org.embeddedt.embeddium.impl.gl.shader.ShaderParser; @@ -81,15 +82,7 @@ class TerrainShaderLinkTest { private static int compile(int type, String name, ShaderConstants constants) { final String src = ShaderParser.parseShader(ShaderLoader.getShaderSource(name), ShaderLoader::getShaderSource, constants); - final int shader = GLStateManager.glCreateShader(type); - GLStateManager.glShaderSource(shader, src); - GLStateManager.glCompileShader(shader); - if (GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS) != GL11.GL_TRUE) { - final String log = GLStateManager.glGetShaderInfoLog(shader, 8192); - GLStateManager.glDeleteShader(shader); - throw new AssertionError(name + " failed to compile with " + constants.getDefineStrings() + ":\n" + log); - } - return shader; + return GlTestSupport.compile(type, src); } @ParameterizedTest(name = "fog={0} vertex={1} filter={2} noMips={3}") diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/TreeSortCameraBookkeepingGLTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/TreeSortCameraBookkeepingGLTest.java index 3ab836a81..c08bbd6d1 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/TreeSortCameraBookkeepingGLTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/TreeSortCameraBookkeepingGLTest.java @@ -1,6 +1,7 @@ package com.gtnewhorizons.angelica.rendering.celeritas; import com.gtnewhorizons.angelica.glsm.GLCoreTest; +import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import org.embeddedt.embeddium.impl.gl.device.CommandList; import org.embeddedt.embeddium.impl.gl.device.RenderDevice; import org.embeddedt.embeddium.impl.render.chunk.ChunkRenderMatrices; @@ -32,8 +33,6 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.lang.reflect.Field; -import java.util.Collection; import java.util.List; import java.util.Map; @@ -109,10 +108,7 @@ class TreeSortCameraBookkeepingGLTest { rsm.onSectionAdded(0, 0, 0); section = rsm.getAllRenderSections().iterator().next(); - final Field f = RenderSectionManager.class.getDeclaredField("cutPlaneIndex"); - f.setAccessible(true); - @SuppressWarnings("unchecked") - final CutPlaneIndex index = (CutPlaneIndex) f.get(rsm); + final CutPlaneIndex index = Reflect.get(rsm, "cutPlaneIndex"); index.put(section, new float[][] { { 8f }, {}, {} }, section.getOriginX(), section.getOriginY(), section.getOriginZ()); assertEquals(1, index.size()); } diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/threading/ThreadedCanRenderOffThreadTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/threading/ThreadedCanRenderOffThreadTest.java index 532db2684..f0c458aeb 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/threading/ThreadedCanRenderOffThreadTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/celeritas/threading/ThreadedCanRenderOffThreadTest.java @@ -6,6 +6,7 @@ import java.util.concurrent.atomic.AtomicReference; import com.gtnewhorizons.angelica.api.ThreadSafeISBRH; import com.gtnewhorizons.angelica.api.ThreadSafeISBRHFactory; +import com.gtnewhorizons.angelica.glsm.testutil.TestThreads; import com.gtnewhorizons.angelica.rendering.IsbrhDispatch; import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler; import net.minecraft.block.Block; @@ -97,9 +98,7 @@ class ThreadedCanRenderOffThreadTest { assertSame(shared, IsbrhDispatch.resolve(handlers, 1, true)); AtomicReference offThread = new AtomicReference<>(); - Thread t = new Thread(() -> offThread.set(IsbrhDispatch.resolve(handlers, 1, false))); - t.start(); - t.join(); + TestThreads.run("off-thread", () -> offThread.set(IsbrhDispatch.resolve(handlers, 1, false))); assertSame(shared, offThread.get()); } @@ -118,9 +117,7 @@ class ThreadedCanRenderOffThreadTest { assertSame(first, sameClassOtherId); AtomicReference spawned = new AtomicReference<>(); - Thread t = new Thread(() -> spawned.set(IsbrhDispatch.resolve(handlers, 2, false))); - t.start(); - t.join(); + TestThreads.run("off-thread", () -> spawned.set(IsbrhDispatch.resolve(handlers, 2, false))); assertNotSame(first, spawned.get()); } diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/CullingShaderCompileTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/CullingShaderCompileTest.java index 65e61bf8e..f3ed7fc93 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/CullingShaderCompileTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/CullingShaderCompileTest.java @@ -2,17 +2,15 @@ package com.gtnewhorizons.angelica.rendering.culling; import com.gtnewhorizons.angelica.glsm.GLCoreTest; import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import com.gtnewhorizons.angelica.glsm.RenderSystem; import org.junit.jupiter.api.Test; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL43; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -28,15 +26,7 @@ class CullingShaderCompileTest { private static void assertCompiles(String name) throws IOException { assumeTrue(RenderSystem.supportsCompute(), "compute shaders unsupported"); - final int shader = GLStateManager.glCreateShader(GL43.GL_COMPUTE_SHADER); - try { - GLStateManager.glShaderSource(shader, source(name)); - GLStateManager.glCompileShader(shader); - final int status = GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS); - assertEquals(GL11.GL_TRUE, status, () -> name + " failed to compile:\n" + GLStateManager.glGetShaderInfoLog(shader, 4096)); - } finally { - GLStateManager.glDeleteShader(shader); - } + GLStateManager.glDeleteShader(GlTestSupport.compile(GL43.GL_COMPUTE_SHADER, source(name))); } @Test diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/FrustumExtractorTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/FrustumExtractorTest.java index 80edacfbe..cb1d0b799 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/FrustumExtractorTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/FrustumExtractorTest.java @@ -20,12 +20,6 @@ class FrustumExtractorTest { private static final double BOUNDARY_SKIP = 1e-3; private static final int SWEEP_SECTIONS = 6; - @Test - void uboSizeIs160() { - assertEquals(160, FrustumExtractor.UBO_SIZE_BYTES); - assertEquals(0, FrustumExtractor.UBO_SIZE_BYTES % 16, "std140 requires a 16-byte multiple"); - } - @Test void patchBatchEntryBase_writesAtOffset128() { final ByteBuffer out = FrustumExtractor.allocateUboByteBuffer(); diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/IndirectCullDrawParityTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/IndirectCullDrawParityTest.java index 1b91bc88b..52351fe90 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/IndirectCullDrawParityTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/IndirectCullDrawParityTest.java @@ -1,5 +1,7 @@ package com.gtnewhorizons.angelica.rendering.culling; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; +import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import org.embeddedt.embeddium.impl.render.chunk.data.SectionRenderDataUnsafe; import org.embeddedt.embeddium.impl.render.chunk.multidraw.BatchAssembler; import org.embeddedt.embeddium.impl.render.chunk.compile.sorting.QuadPrimitiveType; @@ -34,11 +36,9 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL43; -import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; @@ -185,7 +185,7 @@ class IndirectCullDrawParityTest { static void setUpDevice() { RenderDevice.enterManagedCode(); commandList = RenderDevice.INSTANCE.createCommandList(); - program = buildProgram(); + program = GlTestSupport.program(FLAT_VS, GlTestSupport.TRIVIAL_FS); } @AfterAll @@ -202,33 +202,9 @@ class IndirectCullDrawParityTest { GLStateManager.glViewport(0, 0, 800, 600); } - static final String FLAT_VS = "#version 330 core\n" + private static final String FLAT_VS = "#version 330 core\n" + "layout(location = 0) in vec3 a_Pos;\n" + "void main() { gl_Position = vec4(a_Pos, 1.0); }\n"; - static final String FLAT_FS = "#version 330 core\n" - + "out vec4 fragColor;\n" - + "void main() { fragColor = vec4(1.0); }\n"; - - static int compileShader(int type, String src) { - final int shader = GLStateManager.glCreateShader(type); - GLStateManager.glShaderSource(shader, src); - GLStateManager.glCompileShader(shader); - assertEquals(GL11.GL_TRUE, GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS), () -> "shader failed to compile:\n" + GLStateManager.glGetShaderInfoLog(shader, 4096)); - return shader; - } - - private static int buildProgram() { - final int vsh = compileShader(GL20.GL_VERTEX_SHADER, FLAT_VS); - final int fsh = compileShader(GL20.GL_FRAGMENT_SHADER, FLAT_FS); - final int prog = GLStateManager.glCreateProgram(); - GLStateManager.glAttachShader(prog, vsh); - GLStateManager.glAttachShader(prog, fsh); - GLStateManager.glLinkProgram(prog); - assertEquals(GL11.GL_TRUE, GLStateManager.glGetProgrami(prog, GL20.GL_LINK_STATUS), () -> "parity program failed to link:\n" + GLStateManager.glGetProgramInfoLog(prog, 4096)); - GLStateManager.glDeleteShader(vsh); - GLStateManager.glDeleteShader(fsh); - return prog; - } private static Scene buildScene() { final Scene scene = new Scene(); @@ -388,13 +364,7 @@ class IndirectCullDrawParityTest { } private static void setFrustumUbo(GpuTerrainCuller culler, ByteBuffer ubo) { - try { - final Field field = GpuTerrainCuller.class.getDeclaredField("frustumUboBytes"); - field.setAccessible(true); - field.set(culler, ubo); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException(e); - } + Reflect.set(culler, "frustumUboBytes", ubo); } private static void executeRegion(GpuTerrainCuller culler, RenderRegion region, GlVertexArrayTessellation tessellation) { @@ -405,33 +375,15 @@ class IndirectCullDrawParityTest { } private static int reflectInt(GpuTerrainCuller emitter, String fieldName) { - try { - final Field field = GpuTerrainCuller.class.getDeclaredField(fieldName); - field.setAccessible(true); - return field.getInt(emitter); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException(e); - } + return Reflect.get(emitter, fieldName); } private static Object reflectPassState(GpuTerrainCuller gpu, String fieldName) { - try { - final Field field = GpuTerrainCuller.class.getDeclaredField(fieldName); - field.setAccessible(true); - return field.get(gpu); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException(e); - } + return Reflect.get(gpu, fieldName); } private static boolean reflectDispatched(Object passState) { - try { - final Field field = passState.getClass().getDeclaredField("dispatched"); - field.setAccessible(true); - return field.getBoolean(passState); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException(e); - } + return Reflect.get(passState, "dispatched"); } private static ByteBuffer frustumUbo() { diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/MultiDrawCompatStompTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/MultiDrawCompatStompTest.java index 9813fff31..c2288f952 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/MultiDrawCompatStompTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/MultiDrawCompatStompTest.java @@ -3,6 +3,7 @@ package com.gtnewhorizons.angelica.rendering.culling; import com.gtnewhorizons.angelica.glsm.CompatUniformManager; import com.gtnewhorizons.angelica.glsm.GLCoreTest; import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import com.gtnewhorizons.angelica.glsm.RenderSystem; import org.joml.Matrix4f; import org.junit.jupiter.api.AfterEach; @@ -11,7 +12,6 @@ import org.junit.jupiter.api.Test; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL40; import java.nio.ByteBuffer; @@ -34,8 +34,6 @@ class MultiDrawCompatStompTest { + "layout(location = 0) in vec3 a_Pos;\n" + "void main() { gl_Position = iris_ModelViewMatrix * vec4(a_Pos, 1.0); }\n"; - private int vsh; - private int fsh; private int program; private int vao; private int indirectBuffer; @@ -56,13 +54,7 @@ class MultiDrawCompatStompTest { @BeforeEach void armSentinel() { - vsh = IndirectCullDrawParityTest.compileShader(GL20.GL_VERTEX_SHADER, IRIS_MVM_VS); - fsh = IndirectCullDrawParityTest.compileShader(GL20.GL_FRAGMENT_SHADER, IndirectCullDrawParityTest.FLAT_FS); - program = GLStateManager.glCreateProgram(); - GLStateManager.glAttachShader(program, vsh); - GLStateManager.glAttachShader(program, fsh); - GLStateManager.glLinkProgram(program); - assertEquals(GL11.GL_TRUE, GLStateManager.glGetProgrami(program, GL20.GL_LINK_STATUS), () -> "program failed to link:\n" + GLStateManager.glGetProgramInfoLog(program, 4096)); + program = GlTestSupport.program(IRIS_MVM_VS, GlTestSupport.TRIVIAL_FS); assertTrue(CompatUniformManager.hasProgram(program), "program with iris_ModelViewMatrix was not registered by CompatUniformManager at link"); location = GLStateManager.glGetUniformLocation(program, "iris_ModelViewMatrix"); assertTrue(location >= 0, "iris_ModelViewMatrix location not found"); @@ -96,8 +88,6 @@ class MultiDrawCompatStompTest { if (indirectBuffer != 0) GLStateManager.glDeleteBuffers(indirectBuffer); if (elementBuffer != 0) GLStateManager.glDeleteBuffers(elementBuffer); GLStateManager.glDeleteProgram(program); - GLStateManager.glDeleteShader(vsh); - GLStateManager.glDeleteShader(fsh); final FloatBuffer identity = BufferUtils.createFloatBuffer(16); new Matrix4f().get(identity); GLStateManager.glLoadMatrix(identity); diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/SectionMetaBufferTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/SectionMetaBufferTest.java index 65c9a3254..87cd5de99 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/SectionMetaBufferTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/SectionMetaBufferTest.java @@ -1,5 +1,6 @@ package com.gtnewhorizons.angelica.rendering.culling; +import com.gtnewhorizons.angelica.glsm.testutil.TestThreads; import org.embeddedt.embeddium.impl.render.chunk.data.SectionRenderDataUnsafe; import org.embeddedt.embeddium.impl.render.chunk.compile.sorting.QuadPrimitiveType; import com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities; @@ -236,45 +237,37 @@ class SectionMetaBufferTest { final int[] zeros = { 0, 0, 0, 0, 0, 0, 0 }; final ByteBuffer src = SyntheticSectionData.row(0, 0, zeros, zeros, zeros); final AtomicBoolean stop = new AtomicBoolean(false); - final AtomicReference error = new AtomicReference<>(); final long srcAddr = MemoryUtilities.memAddress(src); - final Thread writer = new Thread(() -> { - try { - int x = 0, y = 0, z = 0; - while (!stop.get()) { - meta.update(0, x << 4, y << 4, z << 4, 0, srcAddr, 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED); - x++; - if (x > 63) { x = 0; y++; } - if (y > 63) { y = 0; z++; } - if (z > 63) z = 0; - } - } catch (Throwable t) { error.set(t); } - }, "section-writer"); + final TestThreads.Worker writer = TestThreads.start("section-writer", () -> { + int x = 0, y = 0, z = 0; + while (!stop.get()) { + meta.update(0, x << 4, y << 4, z << 4, 0, srcAddr, 0, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED); + x++; + if (x > 63) { x = 0; y++; } + if (y > 63) { y = 0; z++; } + if (z > 63) z = 0; + } + }); - final Thread reader = new Thread(() -> { - try { - byte[] sink = new byte[1024 * 1024]; - while (!stop.get()) { - meta.syncIfDirty(b -> { - final int n = Math.min(b.remaining(), sink.length); - for (int i = 0; i < n; i++) sink[i] = b.get(i); - return true; - }); - } - } catch (Throwable t) { error.set(t); } - }, "section-reader"); - - writer.start(); - reader.start(); - Thread.sleep(2000); - stop.set(true); - writer.join(5000); - reader.join(5000); + final TestThreads.Worker reader = TestThreads.start("section-reader", () -> { + byte[] sink = new byte[1024 * 1024]; + while (!stop.get()) { + meta.syncIfDirty(b -> { + final int n = Math.min(b.remaining(), sink.length); + for (int i = 0; i < n; i++) sink[i] = b.get(i); + return true; + }); + } + }); try { - if (error.get() != null) throw new AssertionError("worker crashed", error.get()); + Thread.sleep(2000); + stop.set(true); + writer.join(); + reader.join(); } finally { + stop.set(true); MemoryUtilities.memFree(src); meta.reset(); } diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/SectionMetaSlotLifecycleTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/SectionMetaSlotLifecycleTest.java index 6c985d6fa..63b6c2722 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/SectionMetaSlotLifecycleTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/SectionMetaSlotLifecycleTest.java @@ -9,7 +9,6 @@ import java.nio.ByteBuffer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; class SectionMetaSlotLifecycleTest { @@ -52,58 +51,4 @@ class SectionMetaSlotLifecycleTest { meta.reset(); } } - - @Test - void resetReturnsTheBufferToEmpty() { - final SectionMetaBuffer meta = new SectionMetaBuffer(); - final ByteBuffer src = SyntheticSectionData.sliceMaskOnly(0x7F); - final long ptr = MemoryUtilities.memAddress(src); - try { - for (int i = 0; i < 5; i++) meta.update(0, i * 64, 0, 0, 0, ptr, 0x7F, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED); - assertTrue(meta.getHighWaterMark() > 0); - - meta.reset(); - assertEquals(0, meta.getHighWaterMark()); - assertEquals(-1, meta.lookupSlot(0, 0, 0, 0), "slots do not survive a reset"); - - assertEquals(0, meta.update(0, 0, 0, 0, 0, ptr, 0x7F, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED), "allocation restarts from slot 0"); - } finally { - MemoryUtilities.memFree(src); - meta.reset(); - } - } - - @Test - void passesDoNotShareSlots() { - final SectionMetaBuffer meta = new SectionMetaBuffer(); - final ByteBuffer src = SyntheticSectionData.sliceMaskOnly(0x7F); - final long ptr = MemoryUtilities.memAddress(src); - try { - final int solid = meta.update(0, 0, 0, 0, 0, ptr, 0x7F, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED); - final int cutout = meta.update(1, 0, 0, 0, 0, ptr, 0x7F, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED); - final int translucent = meta.update(2, 0, 0, 0, 0, ptr, 0x7F, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED); - assertNotEquals(solid, cutout); - assertNotEquals(cutout, translucent); - assertEquals(3, meta.getHighWaterMark()); - - meta.remove(1, 0, 0, 0, 0); - assertEquals(-1, meta.lookupSlot(1, 0, 0, 0)); - assertEquals(solid, meta.lookupSlot(0, 0, 0, 0), "removing one pass leaves its siblings alone"); - assertEquals(translucent, meta.lookupSlot(2, 0, 0, 0)); - } finally { - MemoryUtilities.memFree(src); - meta.reset(); - } - } - - @Test - void aNullDataPointerAllocatesNothing() { - final SectionMetaBuffer meta = new SectionMetaBuffer(); - try { - assertEquals(-1, meta.update(0, 0, 0, 0, 0, 0L, 0x7F, SectionRenderDataUnsafe.Strategy.FULL, QuadPrimitiveType.TRIANGULATED)); - assertEquals(0, meta.getHighWaterMark()); - } finally { - meta.reset(); - } - } } diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/ShadowSnapshotBlitTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/ShadowSnapshotBlitTest.java index 7ad09ba39..fc816a35a 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/ShadowSnapshotBlitTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/ShadowSnapshotBlitTest.java @@ -3,6 +3,8 @@ package com.gtnewhorizons.angelica.rendering.culling; import com.gtnewhorizons.angelica.glsm.GLCoreTest; import com.gtnewhorizons.angelica.glsm.GLStateManager; import com.gtnewhorizons.angelica.glsm.RenderSystem; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; @@ -19,6 +21,28 @@ class ShadowSnapshotBlitTest { private static final float FAR = 1.0f; private static final float NEAR_PATCH = 0.25f; + private int srcTex; + private int srcFbo; + private int cacheTex; + private int cacheFbo; + + @BeforeEach + void createTargets() { + srcTex = newDepthTexture(); + srcFbo = newDepthFbo(srcTex); + cacheTex = newDepthTexture(); + cacheFbo = newDepthFbo(cacheTex); + } + + @AfterEach + void deleteTargets() { + GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0); + GLStateManager.glDeleteFramebuffers(srcFbo); + GLStateManager.glDeleteFramebuffers(cacheFbo); + GLStateManager.glDeleteTextures(srcTex); + GLStateManager.glDeleteTextures(cacheTex); + } + private static int newDepthTexture() { final int tex = GLStateManager.glGenTextures(); GLStateManager.glBindTexture(GL11.GL_TEXTURE_2D, tex); @@ -59,91 +83,55 @@ class ShadowSnapshotBlitTest { @Test void depthSnapshotRoundTripsThroughBlit() { - final int srcTex = newDepthTexture(); - final int srcFbo = newDepthFbo(srcTex); - final int cacheTex = newDepthTexture(); - final int cacheFbo = newDepthFbo(cacheTex); - try { - fillPattern(srcFbo); - assertEquals(NEAR_PATCH, readDepth(srcFbo, 10, 10), 1e-4f, "source pattern not written"); - assertEquals(FAR, readDepth(srcFbo, SIZE - 10, SIZE - 10), 1e-4f, "source background not written"); + fillPattern(srcFbo); + assertEquals(NEAR_PATCH, readDepth(srcFbo, 10, 10), 1e-4f, "source pattern not written"); + assertEquals(FAR, readDepth(srcFbo, SIZE - 10, SIZE - 10), 1e-4f, "source background not written"); - RenderSystem.blitFramebuffer(srcFbo, cacheFbo, 0, 0, SIZE, SIZE, 0, 0, SIZE, SIZE, GL11.GL_DEPTH_BUFFER_BIT, GL11.GL_NEAREST); - assertEquals(NEAR_PATCH, readDepth(cacheFbo, 10, 10), 1e-4f, "capture blit did not copy the near patch into the cache"); - assertEquals(FAR, readDepth(cacheFbo, SIZE - 10, SIZE - 10), 1e-4f, "capture blit did not copy the far background into the cache"); + RenderSystem.blitFramebuffer(srcFbo, cacheFbo, 0, 0, SIZE, SIZE, 0, 0, SIZE, SIZE, GL11.GL_DEPTH_BUFFER_BIT, GL11.GL_NEAREST); + assertEquals(NEAR_PATCH, readDepth(cacheFbo, 10, 10), 1e-4f, "capture blit did not copy the near patch into the cache"); + assertEquals(FAR, readDepth(cacheFbo, SIZE - 10, SIZE - 10), 1e-4f, "capture blit did not copy the far background into the cache"); - GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, srcFbo); - GLStateManager.glDepthMask(true); - GLStateManager.glClearDepth(FAR); - GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT); - assertEquals(FAR, readDepth(srcFbo, 10, 10), 1e-4f, "source not cleared before restore"); + GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, srcFbo); + GLStateManager.glDepthMask(true); + GLStateManager.glClearDepth(FAR); + GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT); + assertEquals(FAR, readDepth(srcFbo, 10, 10), 1e-4f, "source not cleared before restore"); - RenderSystem.blitFramebuffer(cacheFbo, srcFbo, 0, 0, SIZE, SIZE, 0, 0, SIZE, SIZE, GL11.GL_DEPTH_BUFFER_BIT, GL11.GL_NEAREST); - assertEquals(NEAR_PATCH, readDepth(srcFbo, 10, 10), 1e-4f, "restore blit did not bring the near patch back"); - assertEquals(FAR, readDepth(srcFbo, SIZE - 10, SIZE - 10), 1e-4f, "restore blit corrupted the far background"); - } finally { - GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0); - GLStateManager.glDeleteFramebuffers(srcFbo); - GLStateManager.glDeleteFramebuffers(cacheFbo); - GLStateManager.glDeleteTextures(srcTex); - GLStateManager.glDeleteTextures(cacheTex); - } + RenderSystem.blitFramebuffer(cacheFbo, srcFbo, 0, 0, SIZE, SIZE, 0, 0, SIZE, SIZE, GL11.GL_DEPTH_BUFFER_BIT, GL11.GL_NEAREST); + assertEquals(NEAR_PATCH, readDepth(srcFbo, 10, 10), 1e-4f, "restore blit did not bring the near patch back"); + assertEquals(FAR, readDepth(srcFbo, SIZE - 10, SIZE - 10), 1e-4f, "restore blit corrupted the far background"); } @Test void depthBlitIsNotAffectedByScissorState() { - final int srcTex = newDepthTexture(); - final int srcFbo = newDepthFbo(srcTex); - final int cacheTex = newDepthTexture(); - final int cacheFbo = newDepthFbo(cacheTex); - try { - fillPattern(srcFbo); - GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, cacheFbo); - GLStateManager.glClearDepth(FAR); - GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT); + fillPattern(srcFbo); + GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, cacheFbo); + GLStateManager.glClearDepth(FAR); + GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT); - GLStateManager.glEnable(GL11.GL_SCISSOR_TEST); - GLStateManager.glScissor(0, 0, 4, 4); - RenderSystem.blitFramebuffer(srcFbo, cacheFbo, 0, 0, SIZE, SIZE, 0, 0, SIZE, SIZE, GL11.GL_DEPTH_BUFFER_BIT, GL11.GL_NEAREST); - GLStateManager.glDisable(GL11.GL_SCISSOR_TEST); + GLStateManager.glEnable(GL11.GL_SCISSOR_TEST); + GLStateManager.glScissor(0, 0, 4, 4); + RenderSystem.blitFramebuffer(srcFbo, cacheFbo, 0, 0, SIZE, SIZE, 0, 0, SIZE, SIZE, GL11.GL_DEPTH_BUFFER_BIT, GL11.GL_NEAREST); + GLStateManager.glDisable(GL11.GL_SCISSOR_TEST); - assertEquals(NEAR_PATCH, readDepth(cacheFbo, 100, 100), 1e-4f, "scissor clipped the snapshot blit; the cache only received the scissor box"); - } finally { - GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0); - GLStateManager.glDeleteFramebuffers(srcFbo); - GLStateManager.glDeleteFramebuffers(cacheFbo); - GLStateManager.glDeleteTextures(srcTex); - GLStateManager.glDeleteTextures(cacheTex); - } + assertEquals(NEAR_PATCH, readDepth(cacheFbo, 100, 100), 1e-4f, "scissor clipped the snapshot blit; the cache only received the scissor box"); } @Test void rawBlitStaysScissorClipped() { - final int srcTex = newDepthTexture(); - final int srcFbo = newDepthFbo(srcTex); - final int cacheTex = newDepthTexture(); - final int cacheFbo = newDepthFbo(cacheTex); - try { - fillPattern(srcFbo); - GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, cacheFbo); - GLStateManager.glClearDepth(FAR); - GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT); + fillPattern(srcFbo); + GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, cacheFbo); + GLStateManager.glClearDepth(FAR); + GLStateManager.glClear(GL11.GL_DEPTH_BUFFER_BIT); - GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, srcFbo); - GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, cacheFbo); - GLStateManager.glEnable(GL11.GL_SCISSOR_TEST); - GLStateManager.glScissor(0, 0, 4, 4); - GLStateManager.glBlitFramebuffer(0, 0, SIZE, SIZE, 0, 0, SIZE, SIZE, GL11.GL_DEPTH_BUFFER_BIT, GL11.GL_NEAREST); - GLStateManager.glDisable(GL11.GL_SCISSOR_TEST); + GLStateManager.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, srcFbo); + GLStateManager.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, cacheFbo); + GLStateManager.glEnable(GL11.GL_SCISSOR_TEST); + GLStateManager.glScissor(0, 0, 4, 4); + GLStateManager.glBlitFramebuffer(0, 0, SIZE, SIZE, 0, 0, SIZE, SIZE, GL11.GL_DEPTH_BUFFER_BIT, GL11.GL_NEAREST); + GLStateManager.glDisable(GL11.GL_SCISSOR_TEST); - assertEquals(NEAR_PATCH, readDepth(cacheFbo, 1, 1), 1e-4f, "raw blit copied nothing inside the scissor box"); - assertEquals(FAR, readDepth(cacheFbo, 100, 100), 1e-4f, "raw glBlitFramebuffer ignored scissor"); - } finally { - GLStateManager.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0); - GLStateManager.glDeleteFramebuffers(srcFbo); - GLStateManager.glDeleteFramebuffers(cacheFbo); - GLStateManager.glDeleteTextures(srcTex); - GLStateManager.glDeleteTextures(cacheTex); - } + assertEquals(NEAR_PATCH, readDepth(cacheFbo, 1, 1), 1e-4f, "raw blit copied nothing inside the scissor box"); + assertEquals(FAR, readDepth(cacheFbo, 100, 100), 1e-4f, "raw glBlitFramebuffer ignored scissor"); } } diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/TerrainPassPreparationTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/TerrainPassPreparationTest.java index 46cfc06dc..a086eded4 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/culling/TerrainPassPreparationTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/culling/TerrainPassPreparationTest.java @@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.rendering.culling; import com.gtnewhorizons.angelica.config.GpuCullingMode; import com.gtnewhorizons.angelica.glsm.GLCoreTest; +import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.mixins.interfaces.SectionRenderDataStorageRegionAccessor; import com.gtnewhorizons.angelica.rendering.RenderRegionKeys; import com.gtnewhorizons.angelica.rendering.celeritas.AngelicaRenderPassConfiguration; @@ -24,7 +25,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.lang.reflect.Field; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -53,7 +53,7 @@ class TerrainPassPreparationTest { Storage(boolean sorted) { super(QuadPrimitiveType.TRIANGULATED, sorted, 1); final SectionRenderDataUnsafe.Strategy layout = sorted ? SectionRenderDataUnsafe.Strategy.FULL : SectionRenderDataUnsafe.Strategy.COMPACT; - layout.writeMeshesAndSliceMask((long) field(this, SectionRenderDataStorage.class, "pMeshDataArray"), 0, 0, 0, + layout.writeMeshesAndSliceMask((long) Reflect.get(this, "pMeshDataArray"), 0, 0, 0, Map.of(ModelQuadFacing.VALUES[0], new VertexRange(0, 4)), QuadPrimitiveType.TRIANGULATED); } @@ -64,28 +64,8 @@ class TerrainPassPreparationTest { @Override public int[] angelica$getSlotCache() { return slots; } } - private static Object field(Object target, Class owner, String name) { - try { - final Field field = owner.getDeclaredField(name); - field.setAccessible(true); - return field.get(target); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException(e); - } - } - - private static void set(Object target, Class owner, String name, Object value) { - try { - final Field field = owner.getDeclaredField(name); - field.setAccessible(true); - field.set(target, value); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException(e); - } - } - private Object state(String name) { - return field(gpu, GpuTerrainCuller.class, name); + return Reflect.get(gpu, name); } @BeforeEach @@ -103,18 +83,18 @@ class TerrainPassPreparationTest { AngelicaRenderPassConfiguration.TRANSLUCENT_PASS = passes[2]; GpuCulling.setMode(GpuCullingMode.COMPUTE); nativeCuller = new GpuDrivenChunkCuller(); - set(nativeCuller, GpuDrivenChunkCuller.class, "disabled", true); + Reflect.setDeclared(GpuDrivenChunkCuller.class, nativeCuller, "disabled", true); meta = new SectionMetaBuffer(); gpu = new GpuTerrainCuller(nativeCuller, meta); region = RenderRegionKeys.create(0, 0, 0, 0); - final Map attached = (Map) field(region, RenderRegion.class, "sectionRenderData"); + final Map attached = (Map) Reflect.get(region, "sectionRenderData"); for (int i = 0; i < 3; i++) { storage[i] = new Storage(i == 2); attached.put(passes[i], storage[i]); } list = new ChunkRenderList(region); - set(list, ChunkRenderList.class, "sectionsWithGeometry", new byte[]{0}); - set(list, ChunkRenderList.class, "sectionsWithGeometryCount", 1); + Reflect.setDeclared(ChunkRenderList.class, list, "sectionsWithGeometry", new byte[]{0}); + Reflect.setDeclared(ChunkRenderList.class, list, "sectionsWithGeometryCount", 1); } @AfterEach @@ -154,14 +134,14 @@ class TerrainPassPreparationTest { private void assertRange(String pass, int base) { final Object expected = state(pass); assertSame(expected, state("current")); - assertEquals(base, field(expected, expected.getClass(), "entryBase")); - assertEquals(1, field(expected, expected.getClass(), "entryCount")); - assertEquals(((long) base << 32) | 1L, ((long[]) field(expected, expected.getClass(), "regionRanges"))[0]); + assertEquals(base, Reflect.get(expected, "entryBase")); + assertEquals(1, Reflect.get(expected, "entryCount")); + assertEquals(((long) base << 32) | 1L, ((long[]) Reflect.get(expected, "regionRanges"))[0]); } private int indexPointerMaskOf(String pass) { final Object state = state(pass); - return (int) field(state, state.getClass(), "indexPointerMask"); + return (int) Reflect.get(state, "indexPointerMask"); } @SuppressWarnings("unchecked") @@ -172,7 +152,7 @@ class TerrainPassPreparationTest { passes[2] = pass; storage[2] = newStorage; AngelicaRenderPassConfiguration.TRANSLUCENT_PASS = pass; - final Map attached = (Map) field(region, RenderRegion.class, "sectionRenderData"); + final Map attached = (Map) Reflect.get(region, "sectionRenderData"); attached.put(pass, newStorage); } diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/BatchStateGuardGLTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/BatchStateGuardGLTest.java index 0ecd62528..713b04002 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/BatchStateGuardGLTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/BatchStateGuardGLTest.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.*; @GLCoreTest class BatchStateGuardGLTest { + private final AtomicInteger flushes = new AtomicInteger(); private Runnable previousFlush; @BeforeEach @@ -41,9 +42,12 @@ class BatchStateGuardGLTest { BatchStateGuard.flush = previousFlush; } + private void countFlushes() { + BatchStateGuard.flush = flushes::incrementAndGet; + } + @Test void projectionMutationFlushesBeforeOverwritingTheMatrixAndDoesNotReenter() { - final AtomicInteger flushes = new AtomicInteger(); BatchStateGuard.flush = () -> { flushes.incrementAndGet(); assertEquals(1f, GLStateManager.getProjectionMatrix().m00()); @@ -58,8 +62,7 @@ class BatchStateGuardGLTest { @Test void commonCapturedStateAndManagedPassSetupDoNotDrainBatches() { - final AtomicInteger flushes = new AtomicInteger(); - BatchStateGuard.flush = flushes::incrementAndGet; + countFlushes(); GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); GLStateManager.glPushMatrix(); GLStateManager.glTranslatef(1, 2, 3); @@ -84,7 +87,6 @@ class BatchStateGuardGLTest { assertEquals(29f, GLStateManager.getFogState().getStart()); BatchStateGuard.flush = null; GLStateManager.glDisable(GL11.GL_STENCIL_TEST); - final AtomicInteger flushes = new AtomicInteger(); BatchStateGuard.flush = () -> { assertFalse(GLStateManager.glIsEnabled(GL11.GL_STENCIL_TEST)); flushes.incrementAndGet(); @@ -100,8 +102,7 @@ class BatchStateGuardGLTest { GLStateManager.glPushAttrib(GL11.GL_TEXTURE_BIT); final int texture = GLStateManager.glGenTextures(); try { - final AtomicInteger flushes = new AtomicInteger(); - BatchStateGuard.flush = () -> { + BatchStateGuard.flush = () -> { assertEquals(0, GLStateManager.getBoundTextureForServerState(2)); flushes.incrementAndGet(); }; @@ -124,8 +125,7 @@ class BatchStateGuardGLTest { @Test void compileOnlyDefersTheBarrierUntilPlayback() { final int list = GLStateManager.glGenLists(1); - final AtomicInteger flushes = new AtomicInteger(); - BatchStateGuard.flush = flushes::incrementAndGet; + countFlushes(); try { GLStateManager.glNewList(list, GL11.GL_COMPILE); GLStateManager.glScissor(1, 2, 3, 4); @@ -140,8 +140,7 @@ class BatchStateGuardGLTest { @Test void perUnitCapsFlushExceptTheTexturesBatchLayersOwn() { - final AtomicInteger flushes = new AtomicInteger(); - BatchStateGuard.flush = flushes::incrementAndGet; + countFlushes(); try { GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); GLStateManager.glDisable(GL11.GL_TEXTURE_2D); @@ -166,7 +165,7 @@ class BatchStateGuardGLTest { GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); GLStateManager.glDisable(GL11.GL_TEXTURE_GEN_S); flushes.set(0); - BatchStateGuard.flush = flushes::incrementAndGet; + countFlushes(); GLStateManager.glEnable(GL11.GL_TEXTURE_GEN_S); assertEquals(1, flushes.get()); } finally { @@ -177,40 +176,34 @@ class BatchStateGuardGLTest { @Test void rasterStateNotStoredWithLayersFlushesBeforeChanging() { - final AtomicInteger flushes = new AtomicInteger(); - try { - BatchStateGuard.flush = () -> { - assertEquals(GL11.GL_FILL, GLStateManager.getPolygonState().getFrontMode()); - flushes.incrementAndGet(); - }; - GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE); - GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE); - assertEquals(1, flushes.get()); + BatchStateGuard.flush = () -> { + assertEquals(GL11.GL_FILL, GLStateManager.getPolygonState().getFrontMode()); + flushes.incrementAndGet(); + }; + GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE); + GLStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE); + assertEquals(1, flushes.get()); - flushes.set(0); - BatchStateGuard.flush = () -> { - assertEquals(1.0, GLStateManager.getViewportState().depthRangeFar); - flushes.incrementAndGet(); - }; - GLStateManager.glDepthRange(0.0, 0.5); - GLStateManager.glDepthRange(0.0, 0.5); - assertEquals(1, flushes.get()); + flushes.set(0); + BatchStateGuard.flush = () -> { + assertEquals(1.0, GLStateManager.getViewportState().depthRangeFar); + flushes.incrementAndGet(); + }; + GLStateManager.glDepthRange(0.0, 0.5); + GLStateManager.glDepthRange(0.0, 0.5); + assertEquals(1, flushes.get()); - flushes.set(0); - BatchStateGuard.flush = flushes::incrementAndGet; - GLStateManager.glEnable(GL11.GL_COLOR_LOGIC_OP); - GLStateManager.glLogicOp(GL11.GL_XOR); - GLStateManager.glEnable(GL14.GL_COLOR_SUM); - assertEquals(3, flushes.get()); - } finally { - BatchStateGuard.flush = null; - } + flushes.set(0); + countFlushes(); + GLStateManager.glEnable(GL11.GL_COLOR_LOGIC_OP); + GLStateManager.glLogicOp(GL11.GL_XOR); + GLStateManager.glEnable(GL14.GL_COLOR_SUM); + assertEquals(3, flushes.get()); } @Test void sampleDrawBufferAndDiscardStateFlushesBeforeChanging() { - final AtomicInteger flushes = new AtomicInteger(); - BatchStateGuard.flush = flushes::incrementAndGet; + countFlushes(); final int drawBuffer = GLStateManager.ctx().drawBuffer.getValue(); try { GLStateManager.glEnable(GL13.GL_SAMPLE_ALPHA_TO_COVERAGE); @@ -235,9 +228,8 @@ class BatchStateGuardGLTest { @Test void secondaryColorFlushesOnlyWhileColorSumIsOn() { - final AtomicInteger flushes = new AtomicInteger(); GLStateManager.glSecondaryColor3f(0f, 0f, 0f); - BatchStateGuard.flush = flushes::incrementAndGet; + countFlushes(); try { GLStateManager.glSecondaryColor3f(0.5f, 0f, 0f); assertEquals(0, flushes.get(), "secondary color is unused while color sum is off"); @@ -254,8 +246,7 @@ class BatchStateGuardGLTest { @Test void lightingChangesDoNotDrainBatches() { - final AtomicInteger flushes = new AtomicInteger(); - BatchStateGuard.flush = flushes::incrementAndGet; + countFlushes(); GLStateManager.glEnable(GL11.GL_LIGHT0); GLStateManager.glDisable(GL11.GL_LIGHT0); GLStateManager.glEnable(GL11.GL_COLOR_MATERIAL); @@ -271,7 +262,6 @@ class BatchStateGuardGLTest { GLStateManager.glMatrixMode(GL11.GL_TEXTURE); GLStateManager.glPushMatrix(); GLStateManager.glLoadIdentity(); - final AtomicInteger flushes = new AtomicInteger(); try { BatchStateGuard.flush = () -> { assertEquals(0f, GLStateManager.getTextures().getTextureUnitMatrix(2).m30()); @@ -279,7 +269,7 @@ class BatchStateGuardGLTest { }; GLStateManager.glTranslatef(0.5f, 0, 0); assertEquals(1, flushes.get()); - BatchStateGuard.flush = flushes::incrementAndGet; + countFlushes(); GLStateManager.glActiveTexture(GL13.GL_TEXTURE0); GLStateManager.glPushMatrix(); GLStateManager.glTranslatef(0.5f, 0, 0); diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/ModelBoxCaptureTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/ModelBoxCaptureTest.java index 36aecbf5f..8f5d748d2 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/ModelBoxCaptureTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/ModelBoxCaptureTest.java @@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.rendering.tesr; import com.gtnewhorizons.angelica.glsm.ffp.CubeParams; import com.gtnewhorizons.angelica.glsm.ffp.UnitCubeMesh; +import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import net.coderbot.iris.vertices.IrisQuadView; import net.coderbot.iris.vertices.NormalHelper; import net.minecraft.client.model.ModelBase; @@ -14,7 +15,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -53,13 +53,7 @@ class ModelBoxCaptureTest { } private static TexturedQuad[] quadsOf(ModelBox box) { - try { - final Field field = ModelBox.class.getDeclaredField("quadList"); - field.setAccessible(true); - return (TexturedQuad[]) field.get(box); - } catch (ReflectiveOperationException e) { - throw new AssertionError(e); - } + return Reflect.get(box, "quadList"); } private static CubeParams capture(ModelRenderer part, int texU, int texV, float x, float y, float z, int sizeX, int sizeY, int sizeZ, float inflate) { diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/RetainedTesrGroupsGLTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/RetainedTesrGroupsGLTest.java index 4d1e9c266..df7feb7e0 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/RetainedTesrGroupsGLTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/RetainedTesrGroupsGLTest.java @@ -13,12 +13,13 @@ import com.gtnewhorizons.angelica.glsm.ffp.CubeParityFixture; import com.gtnewhorizons.angelica.glsm.ffp.FfpFixture; import com.gtnewhorizons.angelica.glsm.ffp.Instancing; import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager; +import com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard; import com.gtnewhorizons.angelica.glsm.states.BlendState; import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.rendering.GlintClock; import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrGroups.InstanceColumns; import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrGroups.TexRun; -import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrGroupsTest.CountingPipeline; +import com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.CountingPipeline; import com.gtnewhorizons.angelica.shadercompat.ShaderGlint; import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource; import net.coderbot.iris.layer.PassOverride; @@ -48,7 +49,6 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertFalse; @GLCoreTest class RetainedTesrGroupsGLTest { @@ -113,7 +113,7 @@ class RetainedTesrGroupsGLTest { ring = new InstanceRing(); instanced = new InstancedTemplateRenderer(ring); groups = new RetainedTesrGroups(source); - layer = RetainedTesrGroupsTest.testLayer(); + layer = RetainedTesrTestSupport.testLayer(); } @AfterEach @@ -139,6 +139,8 @@ class RetainedTesrGroupsGLTest { GLStateManager.glLoadIdentity(); GLStateManager.glMatrixMode(GL11.GL_MODELVIEW); GLStateManager.glDisable(GL11.GL_BLEND); + GLStateManager.glDisable(GL11.GL_SCISSOR_TEST); + GLStateManager.disableFog(); GLStateManager.glDepthMask(true); GLStateManager.glDepthFunc(GL11.GL_LEQUAL); GLStateManager.glDisable(GL11.GL_DEPTH_TEST); @@ -187,12 +189,12 @@ class RetainedTesrGroupsGLTest { } private void queueThreeEntities(CountingPipeline pipeline) { - final TemplateBuffer template = RetainedTesrGroupsTest.template(); + final TemplateBuffer template = RetainedTesrTestSupport.template(); groups.beginPass(new Matrix4f(), 0, 0, 0, instanced, pipeline); for (int id = 1; id <= 3; id++) { final Matrix4f mv = new Matrix4f().translation(id, 0f, 0f); - groups.queue(template, layer, RetainedTesrGroupsTest.STREAM, mv, 0, -1, 0, 0L, id, null); - groups.queue(template, RetainedTesrGroupsTest.cubes(1), 0.0625f, layer, RetainedTesrGroupsTest.STREAM, mv, 0, -1, 0, 0L, id, null); + groups.queue(template, layer, RetainedTesrTestSupport.STREAM, mv, 0, -1, 0, 0L, id, null); + groups.queue(template, RetainedTesrTestSupport.cubes(1), 0.0625f, layer, RetainedTesrTestSupport.STREAM, mv, 0, -1, 0, 0L, id, null); } } @@ -213,14 +215,18 @@ class RetainedTesrGroupsGLTest { for (int i = 0; i < 3; i++) { final long info = com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs.packEntityInfo(45010 + i, 45020 + i, 45030 + i); final int color = AngelicaBufferSource.packAbgr(i / 2f, 0, 0, 1); - groups.queue(template, layer, RetainedTesrGroupsTest.STREAM, new Matrix4f(), 0, -1, color, info, 45020 + i, null); + groups.queue(template, layer, RetainedTesrTestSupport.STREAM, new Matrix4f(), 0, -1, color, info, 45020 + i, null); } state.setCurrentEntityAndItem(81, 82); state.setCurrentBlockEntity(83); state.setCurrentEntityColor(0, 1, 0, 0.5f); draws.clear(); skipClientArrays = true; + assertEquals(GL11.GL_QUADS, template.drawMode); groups.drawLayer(layer); + // FIXME each GL_QUADS fallback draw (VertexBuffer.draw inside MeshBuffer.render) raises this with the + // client arrays skipped; a GL_TRIANGLES template does not. + assertEquals(GL11.GL_INVALID_ENUM, GL11.glGetError()); assertEquals(3, draws.size(), "only differing uniform values split the CPU fallback"); for (int i = 0; i < 3; i++) { assertArrayEquals(new long[] {45010 + i, 45020 + i, 45030 + i, @@ -275,7 +281,7 @@ class RetainedTesrGroupsGLTest { @Test void textureMatrixRunsDrawInsideTheSingleBind() { final CountingPipeline pipeline = new CountingPipeline(true, true); - final TemplateBuffer template = RetainedTesrGroupsTest.template(); + final TemplateBuffer template = RetainedTesrTestSupport.template(); final Matrix4f matrixA = new Matrix4f().translation(0.5f, 0f, 0f); final Matrix4f matrixB = new Matrix4f().translation(0f, 0.5f, 0f); final Matrix4f[] texMatrices = {matrixA, matrixB, matrixA}; @@ -283,8 +289,8 @@ class RetainedTesrGroupsGLTest { groups.beginPass(new Matrix4f(), 0, 0, 0, instanced, pipeline); for (int id = 1; id <= 3; id++) { final Matrix4f mv = new Matrix4f().translation(id, 0f, 0f); - groups.queue(template, layer, RetainedTesrGroupsTest.STREAM, mv, 0, -1, 0, 0L, id, texMatrices[id - 1]); - groups.queue(template, RetainedTesrGroupsTest.cubes(1), 0.0625f, layer, RetainedTesrGroupsTest.STREAM, mv, 0, -1, 0, 0L, id, null); + groups.queue(template, layer, RetainedTesrTestSupport.STREAM, mv, 0, -1, 0, 0L, id, texMatrices[id - 1]); + groups.queue(template, RetainedTesrTestSupport.cubes(1), 0.0625f, layer, RetainedTesrTestSupport.STREAM, mv, 0, -1, 0, 0L, id, null); } groups.drawLayer(layer); @@ -298,8 +304,8 @@ class RetainedTesrGroupsGLTest { @Test void interleavedTemplatesBucketOncePerTemplate() { final CountingPipeline pipeline = new CountingPipeline(true, false); - final TemplateBuffer first = RetainedTesrGroupsTest.template(); - final TemplateBuffer second = RetainedTesrGroupsTest.template(); + final TemplateBuffer first = RetainedTesrTestSupport.template(); + final TemplateBuffer second = RetainedTesrTestSupport.template(); groups.beginPass(new Matrix4f(), 0, 0, 0, instanced, pipeline); groups.queue(first, layer, EntityMaterials.GLINT, new Matrix4f().translation(1f, 0f, 0f), 0, -1, 0, 0L, 1, null); @@ -324,10 +330,10 @@ class RetainedTesrGroupsGLTest { for (int id = 1; id <= 3; id++) { final Matrix4f mv = new Matrix4f().translation(id, 0f, 0f); groups.queue(template, layer, EntityMaterials.GLINT, mv, 0, -1, 0, 0L, id, texMatrices[id - 1]); - groups.queue(template, RetainedTesrGroupsTest.cubes(1), 0.0625f, layer, EntityMaterials.GLINT, mv, 0, -1, 0, 0L, id, texMatrices[id - 1]); + groups.queue(template, RetainedTesrTestSupport.cubes(1), 0.0625f, layer, EntityMaterials.GLINT, mv, 0, -1, 0, 0L, id, texMatrices[id - 1]); } - final RetainedTesrGroups.Group group = RetainedTesrGroupsTest.onlyGroup(groups); + final RetainedTesrGroups.Group group = RetainedTesrTestSupport.onlyGroup(groups); assertEquals(2, group.templateColumns.runs.size(), "sanity: template runs A,B,A merge into two heads"); assertNotNull(group.templateColumns.runs.get(0).next, "sanity: the template A head must carry a second segment"); assertEquals(2, group.cubeColumns.runs.size(), "sanity: cube runs A,B,A merge into two heads"); @@ -335,7 +341,6 @@ class RetainedTesrGroupsGLTest { skipClientArrays = true; groups.drawLayer(layer); - while (GL11.glGetError() != GL11.GL_NO_ERROR) {} assertEquals(0, pipeline.matrixBinds); assertEquals(0, pipeline.cubeBinds); @@ -376,111 +381,79 @@ class RetainedTesrGroupsGLTest { assertTrue(identityAtOrigin[0] > 200 && identityAtOrigin[1] < 50 && identityAtOrigin[2] < 50, "an identity instance must render red at the origin, got " + identityAtOrigin[0] + "," + identityAtOrigin[1] + "," + identityAtOrigin[2]); } - @Test - void scissorChangeDrainsQueuedGeometryBeforeClippingAndEndsModelBatching() { + private void queueTriangleOnClearedTarget() { GLStateManager.disableBlend(); GLStateManager.disableAlphaTest(); GLStateManager.disableTexture(); GLStateManager.glDisable(GL11.GL_SCISSOR_TEST); - GLStateManager.glScissor(0, 0, 0, 0); FfpFixture.clear(); groups.beginPass(new Matrix4f(), 0, 0, 0, instanced); - groups.queue(template(TRIANGLE, COLOR_ABGR, GL11.GL_TRIANGLES), layer, RetainedTesrGroupsTest.STREAM, + groups.queue(template(TRIANGLE, COLOR_ABGR, GL11.GL_TRIANGLES), layer, RetainedTesrTestSupport.STREAM, new Matrix4f(), 0, COLOR_ABGR, 0, 0L, 0, null); - final ModelPartBatcher models = ModelPartBatcher.INSTANCE; - final Object oldSource = Reflect.get(models, "activeSource"); - final Object oldGroups = Reflect.get(models, "activeGroups"); - final Runnable oldFlush = com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard.flush; + } + + /** Runs body with the named fields of a batcher singleton swapped and the state fallback installed. */ + private static void withBatcherFields(Object batcher, Runnable body, Object... namesAndValues) { + final Object[] saved = new Object[namesAndValues.length / 2]; + for (int i = 0; i < saved.length; i++) saved[i] = Reflect.get(batcher, (String) namesAndValues[2 * i]); + final Runnable oldFlush = BatchStateGuard.flush; try { - Reflect.set(models, "activeSource", source); - Reflect.set(models, "activeGroups", groups); - Reflect.set(models, "active", true); + for (int i = 0; i < saved.length; i++) Reflect.set(batcher, (String) namesAndValues[2 * i], namesAndValues[2 * i + 1]); BatchStateFallback.install(); + body.run(); + } finally { + for (int i = 0; i < saved.length; i++) Reflect.set(batcher, (String) namesAndValues[2 * i], saved[i]); + BatchStateGuard.flush = oldFlush; + } + } + + @Test + void scissorChangeDrainsQueuedGeometryBeforeClippingAndEndsModelBatching() { + GLStateManager.glScissor(0, 0, 0, 0); + queueTriangleOnClearedTarget(); + final ModelPartBatcher models = ModelPartBatcher.INSTANCE; + withBatcherFields(models, () -> { GLStateManager.glEnable(GL11.GL_SCISSOR_TEST); assertTrue(models.isActive(), "batching must resume for the rest of the pass after the flush"); assertFalse(models.hasQueuedGeometry(), "the flush must drain what was queued before the change"); assertTrue(FfpFixture.readPixel(400, 300)[0] > 200, "queued geometry must draw before the zero-area scissor takes effect"); - } finally { - Reflect.set(models, "active", false); - Reflect.set(models, "activeSource", oldSource); - Reflect.set(models, "activeGroups", oldGroups); - com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard.flush = oldFlush; - GLStateManager.glDisable(GL11.GL_SCISSOR_TEST); - } + }, "activeSource", source, "activeGroups", groups, "active", true); } @Test void heldEntityTranslucentsSurviveUncapturedStateChange() { - GLStateManager.disableBlend(); - GLStateManager.disableAlphaTest(); - GLStateManager.disableTexture(); - GLStateManager.glDisable(GL11.GL_SCISSOR_TEST); GLStateManager.glScissor(0, 0, 0, 0); - FfpFixture.clear(); - groups.beginPass(new Matrix4f(), 0, 0, 0, instanced); - groups.queue(template(TRIANGLE, COLOR_ABGR, GL11.GL_TRIANGLES), layer, RetainedTesrGroupsTest.STREAM, - new Matrix4f(), 0, COLOR_ABGR, 0, 0L, 0, null); + queueTriangleOnClearedTarget(); final ModelPartBatcher models = ModelPartBatcher.INSTANCE; - final Object oldSource = Reflect.get(models, "entitySource"); - final Object oldGroups = Reflect.get(models, "entityGroups"); - final Runnable oldFlush = com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard.flush; - try { - Reflect.set(models, "entitySource", source); - Reflect.set(models, "entityGroups", groups); - Reflect.set(models, "entityTranslucentsHeld", true); - BatchStateFallback.install(); + withBatcherFields(models, () -> { GLStateManager.glEnable(GL11.GL_SCISSOR_TEST); assertTrue(models.hasHeldEntities(), "held entity translucents wait for the post-deferred flush"); assertTrue(FfpFixture.readPixel(400, 300)[0] < 50, "held entity geometry must not draw before deferred"); - } finally { - Reflect.set(models, "entityTranslucentsHeld", false); - Reflect.set(models, "entitySource", oldSource); - Reflect.set(models, "entityGroups", oldGroups); - com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard.flush = oldFlush; - GLStateManager.glDisable(GL11.GL_SCISSOR_TEST); - } + }, "entitySource", source, "entityGroups", groups, "entityTranslucentsHeld", true); } @Test void fogChangeDrainsProviderBatchesAndClearsTheActivePass() { - GLStateManager.disableBlend(); - GLStateManager.disableAlphaTest(); - GLStateManager.disableTexture(); - FfpFixture.clear(); - groups.beginPass(new Matrix4f(), 0, 0, 0, instanced); - groups.queue(template(TRIANGLE, COLOR_ABGR, GL11.GL_TRIANGLES), layer, RetainedTesrGroupsTest.STREAM, - new Matrix4f(), 0, COLOR_ABGR, 0, 0L, 0, null); + queueTriangleOnClearedTarget(); final TesrBatchRenderer renderer = TesrBatchRenderer.INSTANCE; - final Object oldSource = Reflect.get(renderer, "bufferSource"); final RetainedTesrGroups[] retained = Reflect.get(renderer, "retained"); final RetainedTesrGroups oldGroups = retained[0]; - final Runnable oldFlush = com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard.flush; + retained[0] = groups; try { - Reflect.set(renderer, "bufferSource", source); - retained[0] = groups; - Reflect.set(renderer, "activePass", 0); - BatchStateFallback.install(); - GLStateManager.enableFog(); - assertFalse(renderer.hasPendingGeometry()); - assertTrue(FfpFixture.readPixel(400, 300)[0] > 200, "provider geometry must reach the framebuffer before fog is enabled"); + withBatcherFields(renderer, () -> { + GLStateManager.enableFog(); + assertFalse(renderer.hasPendingGeometry()); + assertTrue(FfpFixture.readPixel(400, 300)[0] > 200, "provider geometry must reach the framebuffer before fog is enabled"); + }, "bufferSource", source, "activePass", 0); } finally { - Reflect.set(renderer, "activePass", -1); - Reflect.set(renderer, "bufferSource", oldSource); retained[0] = oldGroups; - com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard.flush = oldFlush; - GLStateManager.disableFog(); } } @Test void emptyShadowPassSetupKeepsBatchingButRendererSpecificStateDisablesIt() { final TesrBatchRenderer renderer = TesrBatchRenderer.INSTANCE; - final Object oldSource = Reflect.get(renderer, "bufferSource"); - final Runnable oldFlush = com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard.flush; - try { - Reflect.set(renderer, "bufferSource", source); - Reflect.set(renderer, "activePass", TesrBatchRenderer.PASS_SHADOW); - BatchStateFallback.install(); + withBatcherFields(renderer, () -> { net.coderbot.iris.layer.GbufferPrograms.setBlockEntityDefaults(); assertTrue(renderer.hasActivePass(), "default attributes before the first renderer must not disable shadow batching"); BatchEligibility.begin(BatchEligibility.SAFE, GLStateManager.drawCalls); @@ -490,11 +463,7 @@ class RetainedTesrGroupsGLTest { } finally { assertEquals(BatchEligibility.DENIED, BatchEligibility.end(BatchEligibility.SAFE, GLStateManager.drawCalls)); } - } finally { - Reflect.set(renderer, "activePass", -1); - Reflect.set(renderer, "bufferSource", oldSource); - com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard.flush = oldFlush; - } + }, "bufferSource", source, "activePass", TesrBatchRenderer.PASS_SHADOW); } @Test @@ -512,8 +481,8 @@ class RetainedTesrGroupsGLTest { GLStateManager.glCullFace(GL11.GL_BACK); GLStateManager.glFrontFace(GL11.GL_CCW); GLStateManager.disableCull(); - final RenderLayer base = TesrLayerStateTest.capturedLayer(EntityMaterials.DROPPED_ITEM_CUTOUT); - final RenderLayer glint = TesrLayerStateTest.capturedLayer( + final RenderLayer base = RetainedTesrTestSupport.capturedLayer(EntityMaterials.DROPPED_ITEM_CUTOUT); + final RenderLayer glint = RetainedTesrTestSupport.capturedLayer( TesrMaterial.builder().glint().depthEqual().noDepthWrite().unlit().stream().build()); for (float yScale : new float[] { -0.9f, 0.9f }) { final Matrix4f mv = new Matrix4f().rotateY(0.55f).scale(0.9f, yScale, 0.9f).translate(-0.5f, -0.5f, 0f); @@ -523,7 +492,6 @@ class RetainedTesrGroupsGLTest { FfpFixture.assertOverlayFootprint(reference[0], reference[1], BACKGROUND, "item glint"); assertArrayEquals(reference[1], queued[1], "glint must cover the same visible faces after batching"); } - assertEquals(GL11.GL_NO_ERROR, GL11.glGetError()); ModelPartBatcher.INSTANCE.clear(); } @@ -566,7 +534,7 @@ class RetainedTesrGroupsGLTest { baseTexture = FfpFixture.solidTexture(60, 60, 60, 255); glintTexture = createGlintTexture(); glintTemplate = capturedQuad(0.35); - glintCubes = RetainedTesrGroupsTest.cubes(1); + glintCubes = RetainedTesrTestSupport.cubes(1); } private static int createGlintTexture() { @@ -872,10 +840,10 @@ class RetainedTesrGroupsGLTest { final int[] reference = FfpFixture.readRegion(SIZE); assertTrue(reference[center] != blank[center], "the mirrored quad must survive glCullFace(GL_FRONT) when drawn immediately, otherwise the parity check is vacuous: " + describe(reference[center])); - final RenderLayer culled = RenderLayer.tesr(null, RetainedTesrGroupsTest.STREAM, PassOverride.NONE, 0f, 0f, ShaderGlint.NO_TINT, cullCode, false, true); + final RenderLayer culled = RenderLayer.tesr(null, RetainedTesrTestSupport.STREAM, PassOverride.NONE, 0f, 0f, ShaderGlint.NO_TINT, cullCode, false, true); FfpFixture.clear(); groups.beginPass(new Matrix4f(), 0, 0, 0, instanced); - groups.queue(quad, culled, RetainedTesrGroupsTest.STREAM, mirrored, 0, COLOR_ABGR, 0, 0L, 1, null); + groups.queue(quad, culled, RetainedTesrTestSupport.STREAM, mirrored, 0, COLOR_ABGR, 0, 0L, 1, null); GLStateManager.glCullFace(GL11.GL_BACK); final boolean cullEnabledBefore = GLStateManager.getCullState().isEnabled(); @@ -933,7 +901,7 @@ class RetainedTesrGroupsGLTest { final RenderLayer opposingLayer = RenderLayer.tesr(null, opposite, PassOverride.NONE, 0f, 0f, ShaderGlint.NO_TINT, DrawState.DISABLED, false, true); groups.beginPass(new Matrix4f(), 0, 0, 0, instanced); - groups.queue(quad, opposingLayer, RetainedTesrGroupsTest.STREAM, new Matrix4f(), 0, COLOR_ABGR, 0, 0L, 1, null); + groups.queue(quad, opposingLayer, RetainedTesrTestSupport.STREAM, new Matrix4f(), 0, COLOR_ABGR, 0, 0L, 1, null); source.endBatch(groups); instanced.endFrame(); diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/RetainedTesrGroupsTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/RetainedTesrGroupsTest.java index 3e69fc5ee..c8fa1b692 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/RetainedTesrGroupsTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/RetainedTesrGroupsTest.java @@ -4,16 +4,13 @@ import com.gtnewhorizons.angelica.api.tesr.TesrMaterial; import com.gtnewhorizons.angelica.compat.mojang.RenderLayer; import com.gtnewhorizons.angelica.glsm.ffp.CubeParams; import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs; -import com.gtnewhorizons.angelica.glsm.ffp.Instancing; import com.gtnewhorizons.angelica.glsm.testutil.Reflect; -import com.gtnewhorizons.angelica.shadercompat.ShaderGlint; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource; import net.coderbot.batchedentityrendering.impl.BufferSegment; import net.coderbot.batchedentityrendering.impl.BufferSourceProbe; import net.coderbot.iris.uniforms.CapturedRenderingState; -import net.coderbot.iris.layer.PassOverride; import org.joml.Matrix4f; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -21,9 +18,12 @@ import org.lwjgl.opengl.GL11; import java.util.List; -import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.COLOR_INDEX; import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.VERTEX_SIZE; -import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.X_INDEX; +import static com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.STREAM; +import static com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.cubes; +import static com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.onlyGroup; +import static com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.template; +import static com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.testLayer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -38,10 +38,6 @@ class RetainedTesrGroupsTest { private RetainedTesrGroups groups; private RenderLayer layer; - static RenderLayer testLayer() { - return RenderLayer.tesr(null, TesrMaterial.CURRENT_STATE, PassOverride.NONE, 0f, 0f, ShaderGlint.NO_TINT, DrawState.DISABLED, false, true); - } - @BeforeEach void freshGroups() { source = new AngelicaBufferSource(); @@ -90,7 +86,7 @@ class RetainedTesrGroupsTest { void instancedGroupsKeepItemIdsPerInstanceWithoutSplitting() { int previous = CapturedRenderingState.INSTANCE.getCurrentRenderedItem(); try { - groups.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring), new CountingPipeline(true, true)); + groups.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring), new RetainedTesrTestSupport.CountingPipeline(true, true)); for (int item : new int[] {42, 43}) { CapturedRenderingState.INSTANCE.setCurrentRenderedItem(item); long info = InstancedAttribs.packEntityInfo(-1, 5, item); @@ -140,7 +136,7 @@ class RetainedTesrGroupsTest { state.pushCurrentBlockEntity(); state.pushCurrentEntityColor(); try { - groups.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring), new CountingPipeline(true, true)); + groups.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring), new RetainedTesrTestSupport.CountingPipeline(true, true)); for (int i = 0; i < 100; i++) { state.setCurrentEntityAndItem(i, i + 200); state.setCurrentBlockEntity(i + 100); @@ -389,27 +385,6 @@ class RetainedTesrGroupsTest { assertTrue(groups.hasDraws(layer)); } - static TemplateBuffer template() { - final int[] data = new int[4 * VERTEX_SIZE]; - for (int v = 0; v < 4; v++) { - final int base = v * VERTEX_SIZE; - data[base + X_INDEX] = Float.floatToRawIntBits(v); - data[base + COLOR_INDEX] = 0xFFFFFFFF; - } - return new TemplateBuffer(data, 4, GL11.GL_QUADS); - } - - static final TesrMaterial STREAM = TesrMaterial.builder().stream().build(); - - static CubeParams[] cubes(int count) { - final CubeParams[] params = new CubeParams[count]; - for (int i = 0; i < count; i++) { - params[i] = CubeParams.of(64f, 32f, i % 2 == 1, i * 8, 0, -4f, -4f, -2f, 8, 8, 4, 0f); - assertNotNull(params[i], "cube " + i + " must pack"); - } - return params; - } - private static int cubeInstances(RetainedTesrGroups groups) { final Reference2ObjectOpenHashMap> byLayer = Reflect.get(groups, "byLayer"); int total = 0; @@ -422,14 +397,6 @@ class RetainedTesrGroupsTest { return total; } - static RetainedTesrGroups.Group onlyGroup(RetainedTesrGroups groups) { - final Reference2ObjectOpenHashMap> byLayer = Reflect.get(groups, "byLayer"); - assertEquals(1, byLayer.size()); - final ObjectArrayList list = byLayer.values().iterator().next(); - assertEquals(1, list.size()); - return list.get(0); - } - @Test void cubePartsRouteToPerPartRecordsThatKeepTheirTemplate() { final TemplateBuffer template = template(); @@ -448,7 +415,7 @@ class RetainedTesrGroupsTest { assertTrue(group.cubeColumns.templates.isEmpty(), "the FFP cube path has no fallback that needs the template"); final RetainedTesrGroups piped = new RetainedTesrGroups(source); - piped.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring), new CountingPipeline(false, false)); + piped.beginPass(new Matrix4f(), 0, 0, 0, new InstancedTemplateRenderer(ring), new RetainedTesrTestSupport.CountingPipeline(false, false)); piped.queue(template, cubes(3), 0.0625f, layer, STREAM, mv, 7, -1, 0, 0L, 5, null); piped.queue(template, cubes(2), 0.0625f, layer, STREAM, new Matrix4f(), 7, -1, 0, 0L, 5, null); @@ -457,43 +424,6 @@ class RetainedTesrGroupsTest { assertSame(template, pipedGroup.cubeColumns.templates.get(0)); } - static final class CountingPipeline implements TesrInstancingPipeline { - - private final boolean matrixVariant; - private final boolean cubeVariant; - int rebinds; - int matrixBinds; - int cubeBinds; - - CountingPipeline(boolean matrixVariant, boolean cubeVariant) { - this.matrixVariant = matrixVariant; - this.cubeVariant = cubeVariant; - } - - @Override - public void rebindCurrentPass() { - rebinds++; - } - - @Override - public boolean hasInstancedVariant(Instancing kind) { - return switch (kind) { - case TEMPLATE -> matrixVariant; - case CUBE -> cubeVariant; - default -> false; - }; - } - - @Override - public void bindInstancedVariant(Instancing kind) { - switch (kind) { - case TEMPLATE -> matrixBinds++; - case CUBE -> cubeBinds++; - default -> throw new AssertionError(kind); - } - } - } - @Test void cubePartsFallBackToTemplatesWithoutAnInstancedRenderer() { diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/RetainedTesrTestSupport.java b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/RetainedTesrTestSupport.java new file mode 100644 index 000000000..b8cf56f9c --- /dev/null +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/RetainedTesrTestSupport.java @@ -0,0 +1,104 @@ +package com.gtnewhorizons.angelica.rendering.tesr; + +import com.gtnewhorizons.angelica.api.tesr.TesrMaterial; +import com.gtnewhorizons.angelica.compat.mojang.RenderLayer; +import com.gtnewhorizons.angelica.glsm.ffp.CubeParams; +import com.gtnewhorizons.angelica.glsm.ffp.Instancing; +import com.gtnewhorizons.angelica.glsm.testutil.Reflect; +import com.gtnewhorizons.angelica.shadercompat.ShaderGlint; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import net.coderbot.iris.layer.PassOverride; +import net.minecraft.util.ResourceLocation; +import org.lwjgl.opengl.GL11; + +import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.COLOR_INDEX; +import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.VERTEX_SIZE; +import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.X_INDEX; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +final class RetainedTesrTestSupport { + + private RetainedTesrTestSupport() {} + + static final TesrMaterial STREAM = TesrMaterial.builder().stream().build(); + + static RenderLayer testLayer() { + return RenderLayer.tesr(null, TesrMaterial.CURRENT_STATE, PassOverride.NONE, 0f, 0f, ShaderGlint.NO_TINT, DrawState.DISABLED, false, true); + } + + static TemplateBuffer template() { + final int[] data = new int[4 * VERTEX_SIZE]; + for (int v = 0; v < 4; v++) { + final int base = v * VERTEX_SIZE; + data[base + X_INDEX] = Float.floatToRawIntBits(v); + data[base + COLOR_INDEX] = 0xFFFFFFFF; + } + return new TemplateBuffer(data, 4, GL11.GL_QUADS); + } + + static CubeParams[] cubes(int count) { + final CubeParams[] params = new CubeParams[count]; + for (int i = 0; i < count; i++) { + params[i] = CubeParams.of(64f, 32f, i % 2 == 1, i * 8, 0, -4f, -4f, -2f, 8, 8, 4, 0f); + assertNotNull(params[i], "cube " + i + " must pack"); + } + return params; + } + + static RetainedTesrGroups.Group onlyGroup(RetainedTesrGroups groups) { + final Reference2ObjectOpenHashMap> byLayer = Reflect.get(groups, "byLayer"); + assertEquals(1, byLayer.size()); + final ObjectArrayList list = byLayer.values().iterator().next(); + assertEquals(1, list.size()); + return list.get(0); + } + + static int liveCull(TesrMaterial material) { + return material.isNoCull() ? DrawState.DISABLED : DrawState.liveCull(); + } + + static RenderLayer capturedLayer(TesrMaterial material) { + return Reflect.invoke(ModelPartBatcher.INSTANCE, "layerFor", + new Class[] { ResourceLocation.class, TesrMaterial.class, PassOverride.class, float.class, float.class, int.class, int.class, boolean.class, boolean.class }, + null, material, PassOverride.NONE, 0f, 0f, ShaderGlint.NO_TINT, liveCull(material), DrawState.liveLit(material), DrawState.liveLightmap()); + } + + static final class CountingPipeline implements TesrInstancingPipeline { + + private final boolean matrixVariant; + private final boolean cubeVariant; + int rebinds; + int matrixBinds; + int cubeBinds; + + CountingPipeline(boolean matrixVariant, boolean cubeVariant) { + this.matrixVariant = matrixVariant; + this.cubeVariant = cubeVariant; + } + + @Override + public void rebindCurrentPass() { + rebinds++; + } + + @Override + public boolean hasInstancedVariant(Instancing kind) { + return switch (kind) { + case TEMPLATE -> matrixVariant; + case CUBE -> cubeVariant; + default -> false; + }; + } + + @Override + public void bindInstancedVariant(Instancing kind) { + switch (kind) { + case TEMPLATE -> matrixBinds++; + case CUBE -> cubeBinds++; + default -> throw new AssertionError(kind); + } + } + } +} diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/StateSetOverrideGLTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/StateSetOverrideGLTest.java index ecadeb160..2115608c7 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/StateSetOverrideGLTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/StateSetOverrideGLTest.java @@ -2,6 +2,7 @@ package com.gtnewhorizons.angelica.rendering.tesr; import com.gtnewhorizons.angelica.glsm.GLCoreTest; import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import com.gtnewhorizons.angelica.glsm.StateSet; import com.gtnewhorizons.angelica.glsm.hooks.GLSMHooks; import com.gtnewhorizons.angelica.glsm.states.AlphaState; @@ -23,6 +24,9 @@ import org.embeddedt.embeddium.impl.render.shader.ShaderLoader; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL20; @@ -31,6 +35,7 @@ import java.lang.reflect.Constructor; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.function.IntSupplier; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -71,14 +76,27 @@ class StateSetOverrideGLTest { if (otherProgram != 0) GLStateManager.glDeleteProgram(otherProgram); } - @Test - void blendBracketUnderOverrideRestoresVanillaShadowAndLeavesLiveStateUntouched() { + /** Vanilla blend is enabled SRC_ALPHA/ONE_MINUS_SRC_ALPHA/ONE/ZERO under an Iris override. */ + private static void overrideVanillaBlend() { VanillaLayerRig.installBlend(); GLStateManager.enableBlend(); GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - BlendModeStorage.overrideBlend(new BlendState(GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE)); + } + + /** Vanilla alpha test is enabled GREATER 0.5 under an Iris override. */ + private static void overrideVanillaAlpha() { + VanillaLayerRig.installAlpha(); + + GLStateManager.enableAlphaTest(); + GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); + AlphaTestStorage.overrideAlphaTest(new AlphaTest(AlphaTestFunction.LEQUAL, 0.25f)); + } + + @Test + void blendBracketUnderOverrideRestoresVanillaShadowAndLeavesLiveStateUntouched() { + overrideVanillaBlend(); final boolean liveEnabledBefore = GLStateManager.getBlendMode().isEnabled(); final BlendState liveBefore = new BlendState().set(GLStateManager.getBlendState()); @@ -105,12 +123,7 @@ class StateSetOverrideGLTest { @Test void alphaBracketUnderOverrideRestoresVanillaShadowAndLeavesLiveStateUntouched() { - VanillaLayerRig.installAlpha(); - - GLStateManager.enableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); - - AlphaTestStorage.overrideAlphaTest(new AlphaTest(AlphaTestFunction.LEQUAL, 0.25f)); + overrideVanillaAlpha(); final boolean liveEnabledBefore = GLStateManager.getAlphaTest().isEnabled(); final int liveFuncBefore = GLStateManager.getAlphaState().getFunction(); @@ -169,12 +182,7 @@ class StateSetOverrideGLTest { @Test void untouchedBracketUnderOverrideLeavesVanillaShadowIntact() { - VanillaLayerRig.installBlend(); - - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - - BlendModeStorage.overrideBlend(new BlendState(GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE)); + overrideVanillaBlend(); final boolean vanillaEnabledBefore = BlendModeStorage.ENABLE_LAYER.getVanilla(); final BlendState vanillaBefore = new BlendState(); @@ -217,11 +225,7 @@ class StateSetOverrideGLTest { @Test void overrideReleasedMidBracket() { - VanillaLayerRig.installAlpha(); - - GLStateManager.enableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); - AlphaTestStorage.overrideAlphaTest(new AlphaTest(AlphaTestFunction.LEQUAL, 0.25f)); + overrideVanillaAlpha(); assertEquals(GL11.GL_LEQUAL, GLStateManager.getAlphaState().getFunction(), "the override value is visible live before the bracket opens"); @@ -280,11 +284,7 @@ class StateSetOverrideGLTest { @Test void glAlphaFuncDeferBranchInsideBracketRestoredAtPop() { - VanillaLayerRig.installAlpha(); - - GLStateManager.enableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); - AlphaTestStorage.overrideAlphaTest(new AlphaTest(AlphaTestFunction.LEQUAL, 0.25f)); + overrideVanillaAlpha(); final int d = GLStateManager.pushState(StateSet.ALPHA); final int liveFuncBefore = GLStateManager.getAlphaState().getFunction(); @@ -300,39 +300,21 @@ class StateSetOverrideGLTest { assertEquals(0.5f, vanillaAfter.getReference(), 0.0001f, "the bracket pop reverts the vanilla alpha-ref shadow to the pre-bracket baseline"); } - @Test - void glBlendFuncDeferBranchInsideBracketRestoredAtPop() { - VanillaLayerRig.installBlend(); - - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - BlendModeStorage.overrideBlend(new BlendState(GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE)); - - final int d = GLStateManager.pushState(StateSet.BLEND); - final int liveSrcBefore = GLStateManager.getBlendState().getSrcRgb(); - - GLStateManager.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO); - - GLStateManager.popStateTo(d); - - assertEquals(liveSrcBefore, GLStateManager.getBlendState().getSrcRgb(), "GLSM's own blend-func cache is untouched by the defer branch, so the bracket pop leaves it exactly as it was"); - final BlendState vanillaAfter = new BlendState(); - BlendModeStorage.FUNC_LAYER.readVanilla(vanillaAfter); - assertEquals(GL11.GL_SRC_ALPHA, vanillaAfter.getSrcRgb(), "the bracket pop reverts the vanilla blend-func shadow to the pre-bracket baseline, undoing the defer branch's write"); + static Stream blendFuncSetters() { + return Stream.of( + Arguments.of("glBlendFunc", (Runnable) () -> GLStateManager.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_ZERO)), + Arguments.of("tryBlendFuncSeparate", (Runnable) () -> GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO))); } - @Test - void tryBlendFuncSeparateDeferBranchInsideBracketRestoredAtPop() { - VanillaLayerRig.installBlend(); - - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - BlendModeStorage.overrideBlend(new BlendState(GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE)); + @ParameterizedTest(name = "{0}") + @MethodSource("blendFuncSetters") + void blendFuncDeferBranchInsideBracketRestoredAtPop(String name, Runnable setBlendFunc) { + overrideVanillaBlend(); final int d = GLStateManager.pushState(StateSet.BLEND); final int liveSrcBefore = GLStateManager.getBlendState().getSrcRgb(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_DST_COLOR, GL11.GL_ZERO, GL11.GL_DST_COLOR, GL11.GL_ZERO); + setBlendFunc.run(); GLStateManager.popStateTo(d); @@ -344,11 +326,7 @@ class StateSetOverrideGLTest { @Test void enableBlendDeferBranchInsideBracketRestoredAtPop() { - VanillaLayerRig.installBlend(); - - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - BlendModeStorage.overrideBlend(new BlendState(GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE)); + overrideVanillaBlend(); final int d = GLStateManager.pushState(StateSet.BLEND); final boolean vanillaBefore = BlendModeStorage.ENABLE_LAYER.getVanilla(); @@ -363,11 +341,7 @@ class StateSetOverrideGLTest { @Test void enableAlphaTestDeferBranchInsideBracketRestoredAtPop() { - VanillaLayerRig.installAlpha(); - - GLStateManager.enableAlphaTest(); - GLStateManager.glAlphaFunc(GL11.GL_GREATER, 0.5f); - AlphaTestStorage.overrideAlphaTest(new AlphaTest(AlphaTestFunction.LEQUAL, 0.25f)); + overrideVanillaAlpha(); final int d = GLStateManager.pushState(StateSet.ALPHA); final boolean vanillaBefore = AlphaTestStorage.ENABLE_LAYER.getVanilla(); @@ -381,11 +355,7 @@ class StateSetOverrideGLTest { @Test void nonMemberBracketUnderOverrideCarriesToParent() { - VanillaLayerRig.installBlend(); - - GLStateManager.enableBlend(); - GLStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); - BlendModeStorage.overrideBlend(new BlendState(GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE)); + overrideVanillaBlend(); final boolean vanillaBefore = BlendModeStorage.ENABLE_LAYER.getVanilla(); @@ -540,23 +510,6 @@ class StateSetOverrideGLTest { } private static int createProgram() { - final int vertex = compile(GL20.GL_VERTEX_SHADER, "alphaRestoreTest.vsh"); - final int fragment = compile(GL20.GL_FRAGMENT_SHADER, "alphaRestoreTest.fsh"); - final int result = GLStateManager.glCreateProgram(); - GLStateManager.glAttachShader(result, vertex); - GLStateManager.glAttachShader(result, fragment); - GLStateManager.glLinkProgram(result); - assertEquals(GL11.GL_TRUE, GLStateManager.glGetProgrami(result, GL20.GL_LINK_STATUS)); - GLStateManager.glDeleteShader(vertex); - GLStateManager.glDeleteShader(fragment); - return result; - } - - private static int compile(int type, String name) { - final int shader = GLStateManager.glCreateShader(type); - GLStateManager.glShaderSource(shader, ShaderLoader.getShaderSource("angelica:" + name)); - GLStateManager.glCompileShader(shader); - assertEquals(GL11.GL_TRUE, GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS)); - return shader; + return GlTestSupport.program(ShaderLoader.getShaderSource("angelica:alphaRestoreTest.vsh"), ShaderLoader.getShaderSource("angelica:alphaRestoreTest.fsh")); } } diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/TesrAnchorMathTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/TesrAnchorMathTest.java index bd9e72ec8..82ebe2c5b 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/TesrAnchorMathTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/TesrAnchorMathTest.java @@ -87,14 +87,4 @@ class TesrAnchorMathTest { final Matrix4f moved = new Matrix4f(mA).translate(1f, 0f, 0f); assertNotEquals(base, TesrAnchorMath.instanceHash(42, moved, 0x00F000F0, -1), "transform"); } - - @Test - void summedHashIsOrderIndependent() { - final Matrix4f m1 = new Matrix4f().translation(1f, 2f, 3f); - final Matrix4f m2 = new Matrix4f().translation(-4f, 5f, -6f).rotateY(0.5f); - final long a = TesrAnchorMath.instanceHash(1, m1, 10, -1); - final long b = TesrAnchorMath.instanceHash(2, m2, 20, -1); - assertEquals(a + b, b + a); - assertNotEquals(a, b); - } } diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/TesrLayerStateTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/TesrLayerStateTest.java index 8b2533ba0..36004816c 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/TesrLayerStateTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/tesr/TesrLayerStateTest.java @@ -6,6 +6,7 @@ import com.gtnewhorizons.angelica.api.tesr.TesrShaders; import com.gtnewhorizons.angelica.compat.mojang.RenderLayer; import com.gtnewhorizons.angelica.glsm.GLCoreTest; import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import com.gtnewhorizons.angelica.glsm.StateSet; import com.gtnewhorizons.angelica.glsm.ffp.ShaderManager; import com.gtnewhorizons.angelica.glsm.hooks.TextureUtilHooks; @@ -25,14 +26,17 @@ import org.joml.Matrix4f; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL13; -import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL30; import java.util.ArrayList; import java.util.List; +import static com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.capturedLayer; +import static com.gtnewhorizons.angelica.rendering.tesr.RetainedTesrTestSupport.liveCull; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotSame; @@ -62,10 +66,7 @@ class TesrLayerStateTest { void tearDown() { AlphaTestStorage.restoreAlphaTest(); DepthColorStorage.unlockDepthColor(); - GLStateManager.getAlphaTest().setVanillaLayer(null); - GLStateManager.getAlphaState().setVanillaLayer(null); - GLStateManager.getDepthState().setVanillaLayer(null); - GLStateManager.getColorMask().setVanillaLayer(null); + VanillaLayerRig.clear(); ModelPartBatcher.INSTANCE.clear(); GLStateManager.glActiveTexture(GL13.GL_TEXTURE1); @@ -239,7 +240,7 @@ class TesrLayerStateTest { @Test void batchDrawSwapsOutAForeignProgramAndFramebuffer() { - final int program = linkTrivialProgram(); + final int program = GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS); final int framebuffer = GLStateManager.glGenFramebuffers(); final AngelicaBufferSource source = new AngelicaBufferSource(); final RenderLayer layer = layer(EntityMaterials.DROPPED_ITEM_CUTOUT); @@ -317,22 +318,6 @@ class TesrLayerStateTest { } } - private static int linkTrivialProgram() { - final int vertex = GLStateManager.glCreateShader(GL20.GL_VERTEX_SHADER); - GLStateManager.glShaderSource(vertex, "#version 330 core\nvoid main() { gl_Position = vec4(0.0); }\n"); - GLStateManager.glCompileShader(vertex); - final int fragment = GLStateManager.glCreateShader(GL20.GL_FRAGMENT_SHADER); - GLStateManager.glShaderSource(fragment, "#version 330 core\nout vec4 color;\nvoid main() { color = vec4(1.0); }\n"); - GLStateManager.glCompileShader(fragment); - final int program = GLStateManager.glCreateProgram(); - GLStateManager.glAttachShader(program, vertex); - GLStateManager.glAttachShader(program, fragment); - GLStateManager.glLinkProgram(program); - GLStateManager.glDeleteShader(vertex); - GLStateManager.glDeleteShader(fragment); - return program; - } - private static RenderLayer glint(int slot) { return RenderLayer.tesr(null, EntityMaterials.GLINT, PassOverride.NONE, 0f, 0f, slot, DrawState.CULL_BACK, true, true); } @@ -356,16 +341,6 @@ class TesrLayerStateTest { assertSame(teardown, thrown.getSuppressed()[0]); } - private static int liveCull(TesrMaterial material) { - return material.isNoCull() ? DrawState.DISABLED : DrawState.liveCull(); - } - - static RenderLayer capturedLayer(TesrMaterial material) { - return Reflect.invoke(ModelPartBatcher.INSTANCE, "layerFor", - new Class[] { ResourceLocation.class, TesrMaterial.class, PassOverride.class, float.class, float.class, int.class, int.class, boolean.class, boolean.class }, - null, material, PassOverride.NONE, 0f, 0f, ShaderGlint.NO_TINT, liveCull(material), DrawState.liveLit(material), DrawState.liveLightmap()); - } - private static RenderLayer queuedLayer(TesrMaterial material) { return Reflect.invoke(TesrBatchRenderer.INSTANCE, "layerFor", new Class[] { ResourceLocation.class, TesrMaterial.class, PassOverride.class, float.class, float.class, int.class, boolean.class, boolean.class }, @@ -439,21 +414,15 @@ class TesrLayerStateTest { assertNotSame(glint(ShaderGlint.NO_TINT), glint(0), "an untinted glint must not share a tinted layer"); } - @Test - void startDrawingLeavesUndeclaredPhasesToTheSurroundingContext() { - layer(TesrMaterial.CURRENT_STATE).startDrawing(); - - assertFalse(GLStateManager.getFogMode().isEnabled(), "fog"); - } - - @Test - void endDrawingLeavesUndeclaredPhasesToTheSurroundingContext() { - GLStateManager.enableFog(); + @ParameterizedTest(name = "fog={0}") + @ValueSource(booleans = { false, true }) + void drawingLeavesUndeclaredPhasesToTheSurroundingContext(boolean fog) { + if (fog) GLStateManager.enableFog(); final RenderLayer layer = layer(TesrMaterial.CURRENT_STATE); layer.startDrawing(); + assertEquals(fog, GLStateManager.getFogMode().isEnabled(), "fog after startDrawing"); layer.endDrawing(); - - assertTrue(GLStateManager.getFogMode().isEnabled(), "fog"); + assertEquals(fog, GLStateManager.getFogMode().isEnabled(), "fog after endDrawing"); } @Test diff --git a/src/test/java/com/gtnewhorizons/angelica/rendering/voxelization/ShadowVoxelizerTest.java b/src/test/java/com/gtnewhorizons/angelica/rendering/voxelization/ShadowVoxelizerTest.java index 7fc4bfdb0..d9b97b529 100644 --- a/src/test/java/com/gtnewhorizons/angelica/rendering/voxelization/ShadowVoxelizerTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/rendering/voxelization/ShadowVoxelizerTest.java @@ -1,5 +1,6 @@ package com.gtnewhorizons.angelica.rendering.voxelization; +import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import com.gtnewhorizons.angelica.rendering.RenderRegionKeys; import org.embeddedt.embeddium.impl.gl.attribute.GlVertexFormat; import org.embeddedt.embeddium.impl.gl.util.VertexRange; @@ -18,7 +19,6 @@ import org.embeddedt.embeddium.impl.render.viewport.CameraTransform; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import java.lang.reflect.Field; import java.util.ArrayList; import java.util.EnumMap; import java.util.Iterator; @@ -88,45 +88,15 @@ class ShadowVoxelizerTest { } private static SectionRenderDataStorage storage(long heap, SectionRenderDataUnsafe.Strategy layout) { - final SectionRenderDataStorage storage = allocateInstance(SectionRenderDataStorage.class); - setField(storage, SectionRenderDataStorage.class, "storageStrategy", layout); - setField(storage, SectionRenderDataStorage.class, "primitiveType", PRIMITIVE); - setField(storage, SectionRenderDataStorage.class, "pMeshDataArray", heap); + final SectionRenderDataStorage storage = Reflect.allocate(SectionRenderDataStorage.class); + Reflect.setDeclared(SectionRenderDataStorage.class, storage, "storageStrategy", layout); + Reflect.setDeclared(SectionRenderDataStorage.class, storage, "primitiveType", PRIMITIVE); + Reflect.setDeclared(SectionRenderDataStorage.class, storage, "pMeshDataArray", heap); return storage; } - @SuppressWarnings("unchecked") - private static T allocateInstance(Class type) { - try { - final Class unsafeClass = Class.forName("sun.misc.Unsafe"); - final Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - final Object unsafe = theUnsafe.get(null); - return (T) unsafeClass.getMethod("allocateInstance", Class.class).invoke(unsafe, type); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException(e); - } - } - - private static void setField(Object target, Class owner, String name, Object value) { - try { - final Field field = owner.getDeclaredField(name); - field.setAccessible(true); - field.set(target, value); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException(e); - } - } - - @SuppressWarnings("unchecked") private static void attachStorage(RenderRegion region, TerrainRenderPass renderPass, SectionRenderDataStorage storage) { - try { - final Field field = RenderRegion.class.getDeclaredField("sectionRenderData"); - field.setAccessible(true); - ((Map) field.get(region)).put(renderPass, storage); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException(e); - } + Reflect.>get(region, "sectionRenderData").put(renderPass, storage); } private static ChunkRenderList renderList(RenderRegion region, int... localSectionIndices) { @@ -135,8 +105,8 @@ class ShadowVoxelizerTest { for (int i = 0; i < localSectionIndices.length; i++) { sections[i] = (byte) localSectionIndices[i]; } - setField(list, ChunkRenderList.class, "sectionsWithGeometry", sections); - setField(list, ChunkRenderList.class, "sectionsWithGeometryCount", localSectionIndices.length); + Reflect.setDeclared(ChunkRenderList.class, list, "sectionsWithGeometry", sections); + Reflect.setDeclared(ChunkRenderList.class, list, "sectionsWithGeometryCount", localSectionIndices.length); return list; } diff --git a/src/test/java/com/gtnewhorizons/angelica/textures/atlas/AtlasPackPolicyTest.java b/src/test/java/com/gtnewhorizons/angelica/textures/atlas/AtlasPackPolicyTest.java index 9d8b6ce8e..10a98d698 100644 --- a/src/test/java/com/gtnewhorizons/angelica/textures/atlas/AtlasPackPolicyTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/textures/atlas/AtlasPackPolicyTest.java @@ -13,6 +13,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Stream; import com.gtnewhorizons.angelica.config.AngelicaConfig; @@ -28,6 +29,9 @@ import net.minecraft.util.ResourceLocation; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; class AtlasPackPolicyTest { @@ -104,28 +108,18 @@ class AtlasPackPolicyTest { } } - @Test - void noPackClaimsAllowsPrefetch() { - final List packs = List.of(new FakePack(new ResourceLocation("tinker", "unrelated.png"))); - assertFalse(AtlasPackPolicy.isClaimed(packs, PNG)); + static Stream claims() { + return Stream.of( + Arguments.of("no pack claims the sprite", new FakePack(new ResourceLocation("tinker", "unrelated.png")), false), + Arguments.of("a pack claims the png", new FakePack(PNG), true), + Arguments.of("a pack claims only the mcmeta", new FakePack(MCMETA), true), + Arguments.of("resourceExists throws", new FakePack(new IllegalStateException("boom")), true)); } - @Test - void packClaimingPngForcesSerial() { - final List packs = List.of(new FakePack(PNG)); - assertTrue(AtlasPackPolicy.isClaimed(packs, PNG)); - } - - @Test - void packClaimingOnlyMcmetaForcesSerial() { - final List packs = List.of(new FakePack(MCMETA)); - assertTrue(AtlasPackPolicy.isClaimed(packs, PNG)); - } - - @Test - void resourceExistsThrowingForcesSerial() { - final List packs = List.of(new FakePack(new IllegalStateException("boom"))); - assertTrue(AtlasPackPolicy.isClaimed(packs, PNG)); + @ParameterizedTest(name = "{0} -> claimed={2}") + @MethodSource("claims") + void isClaimed(String name, IResourcePack pack, boolean expected) { + assertEquals(expected, AtlasPackPolicy.isClaimed(List.of(pack), PNG)); } @Test diff --git a/src/test/java/com/gtnewhorizons/angelica/textures/atlas/MipmapSafetyTest.java b/src/test/java/com/gtnewhorizons/angelica/textures/atlas/MipmapSafetyTest.java index 2895cd843..1ed0226fc 100644 --- a/src/test/java/com/gtnewhorizons/angelica/textures/atlas/MipmapSafetyTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/textures/atlas/MipmapSafetyTest.java @@ -2,12 +2,14 @@ package com.gtnewhorizons.angelica.textures.atlas; import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.util.List; +import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; class MipmapSafetyTest { @@ -38,23 +40,17 @@ class MipmapSafetyTest { public void setFramesTextureData(List data) {} } - @Test - void baseSpriteIsSafe() { - assertTrue(MipmapSafety.isSafe(TextureAtlasSprite.class)); + static Stream spriteClasses() { + return Stream.of( + Arguments.of("base sprite", TextureAtlasSprite.class, true), + Arguments.of("plain subclass", PlainSprite.class, true), + Arguments.of("generateMipmaps override", MipmapOverrideSprite.class, false), + Arguments.of("setFramesTextureData override", SetFramesOverrideSprite.class, false)); } - @Test - void plainSubclassIsSafe() { - assertTrue(MipmapSafety.isSafe(PlainSprite.class)); - } - - @Test - void generateMipmapsOverrideIsUnsafe() { - assertFalse(MipmapSafety.isSafe(MipmapOverrideSprite.class)); - } - - @Test - void setFramesTextureDataOverrideIsUnsafe() { - assertFalse(MipmapSafety.isSafe(SetFramesOverrideSprite.class)); + @ParameterizedTest(name = "{0} -> safe={2}") + @MethodSource("spriteClasses") + void isSafe(String name, Class spriteClass, boolean expected) { + assertEquals(expected, MipmapSafety.isSafe(spriteClass)); } } diff --git a/src/test/java/com/gtnewhorizons/angelica/utils/ConcurrentTileEntityMapTest.java b/src/test/java/com/gtnewhorizons/angelica/utils/ConcurrentTileEntityMapTest.java index 70abd488d..683dbb5c2 100644 --- a/src/test/java/com/gtnewhorizons/angelica/utils/ConcurrentTileEntityMapTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/utils/ConcurrentTileEntityMapTest.java @@ -13,6 +13,8 @@ import net.minecraft.world.ChunkPosition; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import com.gtnewhorizons.angelica.rendering.RenderThreadContext; import com.gtnewhorizons.angelica.rendering.celeritas.world.WorldSlice; @@ -38,23 +40,15 @@ class ConcurrentTileEntityMapTest { return te; } - @Test - void containsKeyAgreesWithGetForAnInvalidTileEntityOnAWorker() { - map.put(POS, tileEntity(true)); - enterWorkerContext(); - - assertNull(map.get(POS), "get must hide an invalid tile entity from a worker"); - assertFalse(map.containsKey(POS), "containsKey must agree with get"); - } - - @Test - void containsKeyAgreesWithGetForAValidTileEntityOnAWorker() { - final TileEntity te = tileEntity(false); + @ParameterizedTest(name = "invalid={0}") + @ValueSource(booleans = { true, false }) + void containsKeyAgreesWithGetOnAWorker(boolean invalid) { + final TileEntity te = tileEntity(invalid); map.put(POS, te); enterWorkerContext(); - assertSame(te, map.get(POS)); - assertTrue(map.containsKey(POS)); + assertSame(invalid ? null : te, map.get(POS), "get must hide only an invalid tile entity from a worker"); + assertEquals(!invalid, map.containsKey(POS), "containsKey must agree with get"); } @Test diff --git a/src/test/java/com/gtnewhorizons/angelica/utils/SpritePaddingTest.java b/src/test/java/com/gtnewhorizons/angelica/utils/SpritePaddingTest.java index bd3829c2e..521498b3b 100644 --- a/src/test/java/com/gtnewhorizons/angelica/utils/SpritePaddingTest.java +++ b/src/test/java/com/gtnewhorizons/angelica/utils/SpritePaddingTest.java @@ -1,8 +1,8 @@ package com.gtnewhorizons.angelica.utils; +import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import org.junit.jupiter.api.Test; -import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Random; @@ -19,13 +19,8 @@ class SpritePaddingTest { private static final byte SENTINEL = (byte) 0xCD; private static int[][] padFrame(int[][] frameData, int width, int height, int gutter) { - try { - final Method method = SpritePadding.class.getDeclaredMethod("padFrame", int[][].class, int.class, int.class, int.class, int[][].class); - method.setAccessible(true); - return (int[][]) method.invoke(null, frameData, width, height, gutter, (Object) null); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } + return Reflect.invokeStatic(SpritePadding.class, "padFrame", new Class[] { int[][].class, int.class, int.class, int.class, int[][].class }, + frameData, width, height, gutter, null); } private static void putLittleEndian(byte[] dst, int offset, int value) { diff --git a/src/test/java/me/flashyreese/mods/reeses_sodium_options/util/StringUtilsTest.java b/src/test/java/me/flashyreese/mods/reeses_sodium_options/util/StringUtilsTest.java index ba03033a2..fedfdb263 100644 --- a/src/test/java/me/flashyreese/mods/reeses_sodium_options/util/StringUtilsTest.java +++ b/src/test/java/me/flashyreese/mods/reeses_sodium_options/util/StringUtilsTest.java @@ -9,6 +9,8 @@ import com.google.common.collect.ImmutableList; import me.jellysquid.mods.sodium.client.gui.options.Option; import me.jellysquid.mods.sodium.client.gui.options.OptionPage; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import java.util.List; @@ -25,61 +27,20 @@ class StringUtilsTest { return new MockOptionPage(ImmutableList.copyOf(options)); } - @Test - void testExactMatch() { - var page = mockPage(mockOption("Max Framerate", "Limits FPS")); - var results = StringUtils.fuzzySearch(List.of(page), "max", 2); - assertEquals(1, results.size()); - } - - @Test - void testFuzzyMatchWithTypo() { - var page = mockPage(mockOption("Max Framerate", "Limits FPS")); - // "fraemrate" is a typo for "framerate" - should match - var results = StringUtils.fuzzySearch(List.of(page), "fraemrate", 2); - assertEquals(1, results.size(), "Should match 'framerate' with typo 'fraemrate'"); - } - - @Test - void testFpsMatchesMaxFramerateViaTooltip() { - var page = mockPage(mockOption("Max Framerate", "Limits the maximum FPS (frames per second).")); - var results = StringUtils.fuzzySearch(List.of(page), "fps", 2); - assertEquals(1, results.size(), "Should match 'Max Framerate' when searching 'fps' via tooltip"); - } - - @Test - void testFpsDoesNotMatchFog() { - var page = mockPage(mockOption("Fog", "Controls fog rendering")); - var results = StringUtils.fuzzySearch(List.of(page), "fps", 2); - assertEquals(0, results.size(), "Should NOT match 'Fog' when searching 'fps'"); - } - - @Test - void testFpsDoesNotMatchFov() { - var page = mockPage(mockOption("Dynamic FOV", "Controls field of view changes")); - var results = StringUtils.fuzzySearch(List.of(page), "fps", 2); - assertEquals(0, results.size(), "Should NOT match 'Dynamic FOV' when searching 'fps'"); - } - - @Test - void testCloudDoesNotMatchMaxFramerate() { - var page = mockPage(mockOption("Max Framerate", "Limits FPS. Reduces system load when multi-tasking.")); - var results = StringUtils.fuzzySearch(List.of(page), "cloud", 2); - assertEquals(0, results.size(), "Should NOT match 'Max Framerate' when searching 'cloud'"); - } - - @Test - void testCloudMatchesClouds() { - var page = mockPage(mockOption("Clouds", "Controls cloud rendering")); - var results = StringUtils.fuzzySearch(List.of(page), "cloud", 2); - assertEquals(1, results.size(), "Should match 'Clouds' when searching 'cloud'"); - } - - @Test - void testPrefixMatch() { - var page = mockPage(mockOption("Brightness", "Controls gamma")); - var results = StringUtils.fuzzySearch(List.of(page), "bri", 2); - assertEquals(1, results.size(), "Should match 'Brightness' with prefix 'bri'"); + @ParameterizedTest(name = "''{2}'' against ''{0}'' -> {3}") + @CsvSource({ + "Max Framerate, Limits FPS, max, 1", + "Max Framerate, Limits FPS, fraemrate, 1", + "Max Framerate, Limits the maximum FPS (frames per second)., fps, 1", + "Fog, Controls fog rendering, fps, 0", + "Dynamic FOV, Controls field of view changes, fps, 0", + "Max Framerate, Limits FPS. Reduces system load when multi-tasking., cloud, 0", + "Clouds, Controls cloud rendering, cloud, 1", + "Brightness, Controls gamma, bri, 1", + }) + void singleOptionSearch(String name, String tooltip, String query, int expectedMatches) { + var page = mockPage(mockOption(name, tooltip)); + assertEquals(expectedMatches, StringUtils.fuzzySearch(List.of(page), query, 2).size()); } @Test diff --git a/src/test/java/net/coderbot/batchedentityrendering/impl/AngelicaBufferSourceTest.java b/src/test/java/net/coderbot/batchedentityrendering/impl/AngelicaBufferSourceTest.java index 26468143c..05840d705 100644 --- a/src/test/java/net/coderbot/batchedentityrendering/impl/AngelicaBufferSourceTest.java +++ b/src/test/java/net/coderbot/batchedentityrendering/impl/AngelicaBufferSourceTest.java @@ -14,12 +14,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class AngelicaBufferSourceTest { - private static final ModelQuad QUAD = SegmentedBufferBuilderTest.quad(); + private static final ModelQuad QUAD = BufferSourceProbe.quad(); @Test void sameLayerKeepsBuilderAffinity() { final AngelicaBufferSource source = new AngelicaBufferSource(); - final RenderLayer layer = SegmentedBufferBuilderTest.layer("a", TransparencyType.OPAQUE); + final RenderLayer layer = BufferSourceProbe.layer("a", TransparencyType.OPAQUE); final SegmentedBufferBuilder first = source.getBuffer(layer, 1); first.addQuad(QUAD); final SegmentedBufferBuilder second = source.getBuffer(layer, 2); @@ -29,8 +29,8 @@ class AngelicaBufferSourceTest { @Test void prepareGroupsSegmentsByLayerAndSortsOpaqueById() { final AngelicaBufferSource source = new AngelicaBufferSource(); - final RenderLayer opaque = SegmentedBufferBuilderTest.layer("opaque", TransparencyType.OPAQUE); - final RenderLayer translucent = SegmentedBufferBuilderTest.layer("translucent", TransparencyType.GENERAL_TRANSPARENT); + final RenderLayer opaque = BufferSourceProbe.layer("opaque", TransparencyType.OPAQUE); + final RenderLayer translucent = BufferSourceProbe.layer("translucent", TransparencyType.GENERAL_TRANSPARENT); source.getBuffer(opaque, 7).addQuad(QUAD); source.getBuffer(translucent, 9).addQuad(QUAD); @@ -60,7 +60,7 @@ class AngelicaBufferSourceTest { final AngelicaBufferSource source = new AngelicaBufferSource(); final int layers = 40; for (int i = 0; i < layers; i++) { - source.getBuffer(SegmentedBufferBuilderTest.layer("layer" + i, TransparencyType.OPAQUE), i).addQuad(QUAD); + source.getBuffer(BufferSourceProbe.layer("layer" + i, TransparencyType.OPAQUE), i).addQuad(QUAD); } final List order = source.prepare(); assertEquals(layers, order.size()); @@ -74,9 +74,9 @@ class AngelicaBufferSourceTest { @Test void endBatchWithTypeCompactsOrder() { final AngelicaBufferSource source = new AngelicaBufferSource(); - final RenderLayer opaqueA = SegmentedBufferBuilderTest.layer("oa", TransparencyType.OPAQUE); - final RenderLayer translucent = SegmentedBufferBuilderTest.layer("t", TransparencyType.GENERAL_TRANSPARENT); - final RenderLayer opaqueB = SegmentedBufferBuilderTest.layer("ob", TransparencyType.OPAQUE); + final RenderLayer opaqueA = BufferSourceProbe.layer("oa", TransparencyType.OPAQUE); + final RenderLayer translucent = BufferSourceProbe.layer("t", TransparencyType.GENERAL_TRANSPARENT); + final RenderLayer opaqueB = BufferSourceProbe.layer("ob", TransparencyType.OPAQUE); source.declareUse(opaqueA); source.declareUse(translucent); source.declareUse(opaqueB); @@ -99,7 +99,7 @@ class AngelicaBufferSourceTest { @Test void declaredLayersAppearInOrderWithoutSegments() { final AngelicaBufferSource source = new AngelicaBufferSource(); - final RenderLayer retainedOnly = SegmentedBufferBuilderTest.layer("retained", TransparencyType.OPAQUE); + final RenderLayer retainedOnly = BufferSourceProbe.layer("retained", TransparencyType.OPAQUE); source.declareUse(retainedOnly); final List order = source.prepare(); assertEquals(1, order.size()); diff --git a/src/test/java/net/coderbot/batchedentityrendering/impl/BufferSourceProbe.java b/src/test/java/net/coderbot/batchedentityrendering/impl/BufferSourceProbe.java index 51af8e630..dec1b84b4 100644 --- a/src/test/java/net/coderbot/batchedentityrendering/impl/BufferSourceProbe.java +++ b/src/test/java/net/coderbot/batchedentityrendering/impl/BufferSourceProbe.java @@ -1,11 +1,19 @@ package net.coderbot.batchedentityrendering.impl; +import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.ModelQuad; +import com.gtnewhorizons.angelica.api.tesr.TesrMaterial; import com.gtnewhorizons.angelica.compat.mojang.RenderLayer; +import com.gtnewhorizons.angelica.rendering.tesr.DrawState; +import com.gtnewhorizons.angelica.shadercompat.ShaderGlint; +import net.coderbot.iris.layer.PassOverride; +import net.minecraft.util.ResourceLocation; import java.util.List; public final class BufferSourceProbe { + private static final TesrMaterial TRANSLUCENT_MATERIAL = TesrMaterial.builder().translucent().build(); + private BufferSourceProbe() {} public static List prepare(AngelicaBufferSource source) { @@ -15,4 +23,19 @@ public final class BufferSourceProbe { public static List segmentsFor(AngelicaBufferSource source, RenderLayer layer) { return source.segmentsFor(layer); } + + public static RenderLayer layer(String name, TransparencyType transparencyType) { + final TesrMaterial material = transparencyType == TransparencyType.OPAQUE ? TesrMaterial.CURRENT_STATE : TRANSLUCENT_MATERIAL; + return RenderLayer.tesr(new ResourceLocation("angelicatest", name), material, PassOverride.NONE, 0f, 0f, ShaderGlint.NO_TINT, DrawState.DISABLED, false, true); + } + + public static ModelQuad quad() { + final ModelQuad q = new ModelQuad(); + for (int i = 0; i < 4; i++) { + q.setX(i, i); + q.setY(i, i); + q.setZ(i, i); + } + return q; + } } diff --git a/src/test/java/net/coderbot/batchedentityrendering/impl/SegmentedBufferBuilderTest.java b/src/test/java/net/coderbot/batchedentityrendering/impl/SegmentedBufferBuilderTest.java index 7828cf876..cfbe56e3d 100644 --- a/src/test/java/net/coderbot/batchedentityrendering/impl/SegmentedBufferBuilderTest.java +++ b/src/test/java/net/coderbot/batchedentityrendering/impl/SegmentedBufferBuilderTest.java @@ -1,14 +1,9 @@ package net.coderbot.batchedentityrendering.impl; import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.ModelQuad; -import com.gtnewhorizons.angelica.api.tesr.TesrMaterial; import com.gtnewhorizons.angelica.compat.mojang.RenderLayer; -import com.gtnewhorizons.angelica.rendering.tesr.DrawState; import com.gtnewhorizons.angelica.rendering.tesr.TemplateBuffer; -import com.gtnewhorizons.angelica.shadercompat.ShaderGlint; -import net.coderbot.iris.layer.PassOverride; import net.coderbot.iris.uniforms.CapturedRenderingState; -import net.minecraft.util.ResourceLocation; import org.joml.Matrix4f; import org.joml.Vector3f; import org.junit.jupiter.api.Test; @@ -21,14 +16,13 @@ import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.CO import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.VERTEX_SIZE; import static com.gtnewhorizon.gtnhlib.client.renderer.cel.util.ModelQuadUtil.X_INDEX; +import static net.coderbot.batchedentityrendering.impl.BufferSourceProbe.layer; +import static net.coderbot.batchedentityrendering.impl.BufferSourceProbe.quad; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class SegmentedBufferBuilderTest { - private static final TesrMaterial OPAQUE_MATERIAL = TesrMaterial.CURRENT_STATE; - private static final TesrMaterial TRANSLUCENT_MATERIAL = TesrMaterial.builder().translucent().build(); - @Test void segmentsSnapshotAllIdsAndTintAtQueueTime() { final CapturedRenderingState state = CapturedRenderingState.INSTANCE; @@ -67,21 +61,6 @@ class SegmentedBufferBuilderTest { } } - static RenderLayer layer(String name, TransparencyType transparencyType) { - final TesrMaterial material = transparencyType == TransparencyType.OPAQUE ? OPAQUE_MATERIAL : TRANSLUCENT_MATERIAL; - return RenderLayer.tesr(new ResourceLocation("angelicatest", name), material, PassOverride.NONE, 0f, 0f, ShaderGlint.NO_TINT, DrawState.DISABLED, false, true); - } - - static ModelQuad quad() { - final ModelQuad q = new ModelQuad(); - for (int i = 0; i < 4; i++) { - q.setX(i, i); - q.setY(i, i); - q.setZ(i, i); - } - return q; - } - @Test void sameKeyContinuesSegment() { final SegmentedBufferBuilder builder = new SegmentedBufferBuilder(); diff --git a/src/test/java/net/coderbot/iris/IrisProgramSetWarmupTest.java b/src/test/java/net/coderbot/iris/IrisProgramSetWarmupTest.java index 37646eb63..476268aef 100644 --- a/src/test/java/net/coderbot/iris/IrisProgramSetWarmupTest.java +++ b/src/test/java/net/coderbot/iris/IrisProgramSetWarmupTest.java @@ -1,6 +1,7 @@ package net.coderbot.iris; import com.gtnewhorizons.angelica.glsm.testutil.Reflect; +import com.gtnewhorizons.angelica.glsm.testutil.TestThreads; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -34,11 +35,10 @@ class IrisProgramSetWarmupTest { final CompletableFuture warmup = new CompletableFuture<>(); Reflect.setStatic(Iris.class, "programSetWarmup", warmup); final CountDownLatch done = new CountDownLatch(1); - final Thread joiner = new Thread(() -> { + final TestThreads.Worker joiner = TestThreads.start("warmup-joiner", () -> { await(); done.countDown(); }); - joiner.start(); assertFalse(done.await(200, TimeUnit.MILLISECONDS)); warmup.complete(null); assertTrue(done.await(5, TimeUnit.SECONDS)); diff --git a/src/test/java/net/coderbot/iris/PackSettingsFingerprintTest.java b/src/test/java/net/coderbot/iris/PackSettingsFingerprintTest.java index d76e6a926..709675736 100644 --- a/src/test/java/net/coderbot/iris/PackSettingsFingerprintTest.java +++ b/src/test/java/net/coderbot/iris/PackSettingsFingerprintTest.java @@ -1,11 +1,11 @@ package net.coderbot.iris; import com.gtnewhorizons.angelica.config.AngelicaConfig; +import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import net.coderbot.iris.shaderpack.StringPair; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -16,21 +16,23 @@ import static org.junit.jupiter.api.Assertions.*; class PackSettingsFingerprintTest { @TempDir Path dir; + private static Object describe(String name, Path pack, Properties properties, Iterable defines, boolean byContent) { + return Reflect.invokeStatic(Iris.class, "describeSettings", new Class[] { String.class, Path.class, Properties.class, Iterable.class, boolean.class }, + name, pack, properties, defines, byContent); + } + @Test void effectiveFallbackVersionInvalidatesPreparedAndCompiledPackSettings() throws Exception { final Path pack = Files.write(dir.resolve("pack.zip"), new byte[] {1}); - final Method describe = Iris.class.getDeclaredMethod("describeSettings", String.class, Path.class, - Properties.class, Iterable.class, boolean.class); - describe.setAccessible(true); final int previous = AngelicaConfig.modernFallbackMcVersion; try { for (boolean byContent : new boolean[] {false, true}) { AngelicaConfig.modernFallbackMcVersion = 0; - final Object defaults = describe.invoke(null, "pack", pack, new Properties(), List.of(new StringPair("MC_VERSION", "10710")), byContent); + final Object defaults = describe("pack", pack, new Properties(), List.of(new StringPair("MC_VERSION", "10710")), byContent); AngelicaConfig.modernFallbackMcVersion = 260101; - assertEquals(defaults, describe.invoke(null, "pack", pack, new Properties(), List.of(new StringPair("MC_VERSION", "10710")), byContent)); + assertEquals(defaults, describe("pack", pack, new Properties(), List.of(new StringPair("MC_VERSION", "10710")), byContent)); AngelicaConfig.modernFallbackMcVersion = 12001; - assertNotEquals(defaults, describe.invoke(null, "pack", pack, new Properties(), List.of(new StringPair("MC_VERSION", "10710")), byContent)); + assertNotEquals(defaults, describe("pack", pack, new Properties(), List.of(new StringPair("MC_VERSION", "10710")), byContent)); } } finally { AngelicaConfig.modernFallbackMcVersion = previous; diff --git a/src/test/java/net/coderbot/iris/gl/program/ProgramBindingRegressionTest.java b/src/test/java/net/coderbot/iris/gl/program/ProgramBindingRegressionTest.java index 13db39eaf..ac731d19c 100644 --- a/src/test/java/net/coderbot/iris/gl/program/ProgramBindingRegressionTest.java +++ b/src/test/java/net/coderbot/iris/gl/program/ProgramBindingRegressionTest.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableSet; import com.gtnewhorizons.angelica.glsm.DisplayListManager; import com.gtnewhorizons.angelica.glsm.GLCoreTest; import com.gtnewhorizons.angelica.glsm.GLStateManager; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import com.gtnewhorizons.angelica.glsm.backend.BackendManager; import com.gtnewhorizons.angelica.glsm.backend.RenderBackend; import com.gtnewhorizons.angelica.glsm.recording.GLCommand; @@ -84,20 +85,7 @@ class ProgramBindingRegressionTest { } private static int linkedProgram() { - int vertex = GLStateManager.glCreateShader(GL20.GL_VERTEX_SHADER); - int fragment = GLStateManager.glCreateShader(GL20.GL_FRAGMENT_SHADER); - GLStateManager.glShaderSource(vertex, "#version 330 core\nvoid main() { gl_Position = vec4(0); }"); - GLStateManager.glShaderSource(fragment, "#version 330 core\nout vec4 color; void main() { color = vec4(1); }"); - GLStateManager.glCompileShader(vertex); - GLStateManager.glCompileShader(fragment); - int program = GLStateManager.glCreateProgram(); - GLStateManager.glAttachShader(program, vertex); - GLStateManager.glAttachShader(program, fragment); - GLStateManager.glLinkProgram(program); - GLStateManager.glDeleteShader(vertex); - GLStateManager.glDeleteShader(fragment); - assertEquals(GL11.GL_TRUE, GLStateManager.glGetProgrami(program, GL20.GL_LINK_STATUS)); - return program; + return GlTestSupport.program(GlTestSupport.TRIVIAL_VS, GlTestSupport.TRIVIAL_FS); } @Test diff --git a/src/test/java/net/coderbot/iris/gl/uniform/ManifestCollectingHolderTest.java b/src/test/java/net/coderbot/iris/gl/uniform/ManifestCollectingHolderTest.java index dc7b260af..6569d8fb7 100644 --- a/src/test/java/net/coderbot/iris/gl/uniform/ManifestCollectingHolderTest.java +++ b/src/test/java/net/coderbot/iris/gl/uniform/ManifestCollectingHolderTest.java @@ -52,11 +52,6 @@ class ManifestCollectingHolderTest { assertEquals(UniformType.INT, entry(m, "worldTime").type()); } - @Test - void isDeterministicAcrossHarvests() { - assertEquals(harvest().entries(), harvest().entries()); - } - @Test void externallyManagedUniformsStayOutOfTheBlock() { final UniformManifest m = harvest(); diff --git a/src/test/java/net/coderbot/iris/pipeline/AttributeTransformerLinkGLTest.java b/src/test/java/net/coderbot/iris/pipeline/AttributeTransformerLinkGLTest.java index 5d6abb072..005ed689c 100644 --- a/src/test/java/net/coderbot/iris/pipeline/AttributeTransformerLinkGLTest.java +++ b/src/test/java/net/coderbot/iris/pipeline/AttributeTransformerLinkGLTest.java @@ -2,15 +2,13 @@ package net.coderbot.iris.pipeline; import com.gtnewhorizons.angelica.glsm.GLCoreTest; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import com.gtnewhorizons.angelica.glsm.RenderSystem; import com.gtnewhorizons.angelica.glsm.shader.ShaderType; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability; import net.coderbot.iris.gl.shader.GlShader; import net.coderbot.iris.gl.shader.ProgramCreator; import net.coderbot.iris.pipeline.transform.PatchShaderType; -import net.coderbot.iris.pipeline.transform.ShaderTransformer; import net.coderbot.iris.pipeline.transform.TransformPatcher; +import net.coderbot.iris.pipeline.transform.TransformTestSupport; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -74,21 +72,17 @@ class AttributeTransformerLinkGLTest { @BeforeAll static void pinGlslVersion() { - previousMaxGlslVersion = RenderSystem.getMaxGlslVersion(); - Reflect.setStatic(RenderSystem.class, "maxGlslVersion", 330); - ShaderTransformer.init(); + previousMaxGlslVersion = TransformTestSupport.pinGlslVersion(330); } @AfterAll static void restoreGlslVersion() { - Reflect.setStatic(RenderSystem.class, "maxGlslVersion", previousMaxGlslVersion); - ShaderTransformer.init(); + TransformTestSupport.restoreGlslVersion(previousMaxGlslVersion); } @BeforeEach void clearCaches() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); + TransformTestSupport.clearCaches(); } private static int link(String name, Map result) { diff --git a/src/test/java/net/coderbot/iris/pipeline/IrisInstancingGLTest.java b/src/test/java/net/coderbot/iris/pipeline/IrisInstancingGLTest.java index cccf3a55f..871c29ca9 100644 --- a/src/test/java/net/coderbot/iris/pipeline/IrisInstancingGLTest.java +++ b/src/test/java/net/coderbot/iris/pipeline/IrisInstancingGLTest.java @@ -2,7 +2,7 @@ package net.coderbot.iris.pipeline; import com.gtnewhorizons.angelica.glsm.GLCoreTest; import com.gtnewhorizons.angelica.glsm.GLStateManager; -import com.gtnewhorizons.angelica.glsm.RenderSystem; +import com.gtnewhorizons.angelica.glsm.GlTestSupport; import com.gtnewhorizons.angelica.glsm.ffp.Instancing; import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs; import com.gtnewhorizons.angelica.glsm.ffp.FfpFixture; @@ -21,8 +21,8 @@ import net.coderbot.iris.uniforms.CapturedRenderingState; import net.coderbot.batchedentityrendering.impl.AngelicaBufferSource; import net.coderbot.iris.pipeline.DeferredWorldRenderingPipeline.Pass; import net.coderbot.iris.pipeline.transform.PatchShaderType; -import net.coderbot.iris.pipeline.transform.ShaderTransformer; import net.coderbot.iris.pipeline.transform.TransformPatcher; +import net.coderbot.iris.pipeline.transform.TransformTestSupport; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -42,6 +42,7 @@ import java.util.Arrays; import java.util.List; import java.util.function.Consumer; +import static net.coderbot.iris.pipeline.transform.TransformTestSupport.TEX_LM; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -51,8 +52,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @GLCoreTest class IrisInstancingGLTest { - private static final InputAvailability TEX_LM = InputAvailability.of(true, true); - private static final String CUBE_VERTEX = """ #version 120 attribute vec4 mc_Entity; @@ -168,21 +167,17 @@ class IrisInstancingGLTest { @BeforeAll static void pinGlslVersion() { - previousMaxGlslVersion = RenderSystem.getMaxGlslVersion(); - Reflect.setStatic(RenderSystem.class, "maxGlslVersion", 330); - ShaderTransformer.init(); + previousMaxGlslVersion = TransformTestSupport.pinGlslVersion(330); } @AfterAll static void restoreGlslVersion() { - Reflect.setStatic(RenderSystem.class, "maxGlslVersion", previousMaxGlslVersion); - ShaderTransformer.init(); + TransformTestSupport.restoreGlslVersion(previousMaxGlslVersion); } @BeforeEach void setUp() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); + TransformTestSupport.clearCaches(); GLStateManager.disableBlend(); } @@ -273,6 +268,8 @@ class IrisInstancingGLTest { final int program = GLStateManager.glCreateProgram(); GLStateManager.glUseProgram(program); + // Never linked, so the driver rejects the bind; GLSM still tracks it as the active program. + assertEquals(GL11.GL_INVALID_OPERATION, GL11.glGetError()); try { final long dc = GLStateManager.drawCalls; BatchEligibility.begin(BatchEligibility.SAFE, dc); @@ -485,10 +482,6 @@ class IrisInstancingGLTest { } private static void compile(int type, String source) { - final int shader = GLStateManager.glCreateShader(type); - GLStateManager.glShaderSource(shader, source); - GLStateManager.glCompileShader(shader); - assertEquals(GL11.GL_TRUE, GLStateManager.glGetShaderi(shader, GL20.GL_COMPILE_STATUS), () -> "shader failed to compile:\n" + GLStateManager.glGetShaderInfoLog(shader, 4096) + "\n" + source); - GLStateManager.glDeleteShader(shader); + GLStateManager.glDeleteShader(GlTestSupport.compile(type, source)); } } diff --git a/src/test/java/net/coderbot/iris/pipeline/MvBuiltinScanTest.java b/src/test/java/net/coderbot/iris/pipeline/MvBuiltinScanTest.java index 3a647efa1..ecf998f17 100644 --- a/src/test/java/net/coderbot/iris/pipeline/MvBuiltinScanTest.java +++ b/src/test/java/net/coderbot/iris/pipeline/MvBuiltinScanTest.java @@ -1,38 +1,27 @@ package net.coderbot.iris.pipeline; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; class MvBuiltinScanTest { - @Test - void detectsVariableUsage() { - String frag = "#version 120\nvoid main() { vec4 p = gl_ModelViewMatrix * vec4(1.0); gl_FragColor = p; }"; - assertTrue(DeferredWorldRenderingPipeline.referencesMvBuiltins(frag)); + static Stream sources() { + return Stream.of( + Arguments.of("variable usage", "#version 120\nvoid main() { vec4 p = gl_ModelViewMatrix * vec4(1.0); gl_FragColor = p; }", true), + Arguments.of("core profile matrix names", "#version 150\nuniform mat4 modelViewMatrixInverse;\nvoid main() { gl_FragData[0] = modelViewMatrixInverse * vec4(1.0); }", true), + Arguments.of("ftransform call", "#version 120\nvoid main() { gl_Position = ftransform(); }", true), + Arguments.of("comment only", "#version 120\n// gl_ModelViewMatrix in a comment only\nvoid main() { gl_FragColor = vec4(1.0); }", false), + Arguments.of("null source", null, false)); } - @Test - void detectsCoreProfileMatrixNames() { - String frag = "#version 150\nuniform mat4 modelViewMatrixInverse;\nvoid main() { gl_FragData[0] = modelViewMatrixInverse * vec4(1.0); }"; - assertTrue(DeferredWorldRenderingPipeline.referencesMvBuiltins(frag)); - } - - @Test - void detectsFtransformCall() { - String vert = "#version 120\nvoid main() { gl_Position = ftransform(); }"; - assertTrue(DeferredWorldRenderingPipeline.referencesMvBuiltins(vert)); - } - - @Test - void ignoresCommentsAndUnrelated() { - String frag = "#version 120\n// gl_ModelViewMatrix in a comment only\nvoid main() { gl_FragColor = vec4(1.0); }"; - assertFalse(DeferredWorldRenderingPipeline.referencesMvBuiltins(frag)); - } - - @Test - void nullSourceIsClean() { - assertFalse(DeferredWorldRenderingPipeline.referencesMvBuiltins(null)); + @ParameterizedTest(name = "{0} -> {2}") + @MethodSource("sources") + void referencesMvBuiltins(String name, String source, boolean expected) { + assertEquals(expected, DeferredWorldRenderingPipeline.referencesMvBuiltins(source)); } } diff --git a/src/test/java/net/coderbot/iris/pipeline/transform/AttributeTransformerInstancedTest.java b/src/test/java/net/coderbot/iris/pipeline/transform/AttributeTransformerInstancedTest.java index 1303fea83..4064ad5ff 100644 --- a/src/test/java/net/coderbot/iris/pipeline/transform/AttributeTransformerInstancedTest.java +++ b/src/test/java/net/coderbot/iris/pipeline/transform/AttributeTransformerInstancedTest.java @@ -5,10 +5,9 @@ import com.gtnewhorizons.angelica.glsm.ffp.CubeInstancedAttribs; import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs; import com.gtnewhorizons.angelica.glsm.ffp.Instancing; import com.gtnewhorizons.angelica.glsm.ffp.ParticleInstancedAttribs; -import com.gtnewhorizons.angelica.glsm.RenderSystem; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability; import net.coderbot.iris.pipeline.transform.parameter.AttributeParameters; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -19,6 +18,8 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static net.coderbot.iris.pipeline.transform.TransformTestSupport.TEX_LM; +import static net.coderbot.iris.pipeline.transform.TransformTestSupport.squash; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -29,8 +30,6 @@ class AttributeTransformerInstancedTest { private static final Pattern ATTRIBUTE_LOCATION = Pattern.compile("layout\\s*\\(\\s*location\\s*=\\s*(\\d+)\\s*\\)\\s*in\\s+\\w+\\s+(\\w+)"); - private static final InputAvailability TEX_LM = InputAvailability.of(true, true); - private static final String PARTICLE_VERTEX = """ #version 120 attribute vec4 mc_Entity; @@ -160,20 +159,21 @@ class AttributeTransformerInstancedTest { } """; + private static int previousMaxGlslVersion; + @BeforeAll - static void initShaderTransformer() throws Exception { - Reflect.setStatic(RenderSystem.class, "maxGlslVersion", 460); - ShaderTransformer.init(); + static void pinGlslVersion() { + previousMaxGlslVersion = TransformTestSupport.pinGlslVersion(460); + } + + @AfterAll + static void restoreGlslVersion() { + TransformTestSupport.restoreGlslVersion(previousMaxGlslVersion); } @BeforeEach void clearCaches() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); - } - - private static String squash(String s) { - return s.replaceAll("\\s+", ""); + TransformTestSupport.clearCaches(); } private static void assertHas(String shader, String snippet) { @@ -229,14 +229,9 @@ class AttributeTransformerInstancedTest { } @Test - void generatedInstancedShaderHasNoOverlappingAttributeLocations() { - final Map byLocation = attributeLocations(instancedVertex(VERTEX)); - assertFalse(byLocation.isEmpty(), "expected the instanced variant to declare attribute locations"); - } - - @Test - void declaresInstanceAttribsAndGlobalMatrices() { + void declaresInstanceAttribsAtDistinctLocationsAndGlobalMatrices() { final String v = instancedVertex(VERTEX); + attributeLocations(v); assertHas(v, "layout(location = " + InstancedAttribs.LOC_ROW0 + ") in vec4 iris_InstRow0"); assertHas(v, "layout(location = " + InstancedAttribs.LOC_ROW1 + ") in vec4 iris_InstRow1"); assertHas(v, "layout(location = " + InstancedAttribs.LOC_ROW2 + ") in vec4 iris_InstRow2"); diff --git a/src/test/java/net/coderbot/iris/pipeline/transform/CompositeVertexWriteExtractionTest.java b/src/test/java/net/coderbot/iris/pipeline/transform/CompositeVertexWriteExtractionTest.java index b1d254e52..6134640b3 100644 --- a/src/test/java/net/coderbot/iris/pipeline/transform/CompositeVertexWriteExtractionTest.java +++ b/src/test/java/net/coderbot/iris/pipeline/transform/CompositeVertexWriteExtractionTest.java @@ -1,7 +1,6 @@ package net.coderbot.iris.pipeline.transform; -import com.gtnewhorizons.angelica.glsm.RenderSystem; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -42,16 +41,21 @@ class CompositeVertexWriteExtractionTest { void main() { gl_FragData[0] = vec4(texCoord, 0.0, 1.0); } """; + private static int previousMaxGlslVersion; + @BeforeAll - static void initShaderTransformer() throws Exception { - Reflect.setStatic(RenderSystem.class, "maxGlslVersion", 460); - ShaderTransformer.init(); + static void pinGlslVersion() { + previousMaxGlslVersion = TransformTestSupport.pinGlslVersion(460); + } + + @AfterAll + static void restoreGlslVersion() { + TransformTestSupport.restoreGlslVersion(previousMaxGlslVersion); } @BeforeEach void clearCaches() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); + TransformTestSupport.clearCaches(); } @Test diff --git a/src/test/java/net/coderbot/iris/pipeline/transform/DHGenericAttributeLocationTest.java b/src/test/java/net/coderbot/iris/pipeline/transform/DHGenericAttributeLocationTest.java index 4556af928..200cb48b4 100644 --- a/src/test/java/net/coderbot/iris/pipeline/transform/DHGenericAttributeLocationTest.java +++ b/src/test/java/net/coderbot/iris/pipeline/transform/DHGenericAttributeLocationTest.java @@ -1,8 +1,7 @@ package net.coderbot.iris.pipeline.transform; -import com.gtnewhorizons.angelica.glsm.RenderSystem; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -45,16 +44,21 @@ class DHGenericAttributeLocationTest { } """; + private static int previousMaxGlslVersion; + @BeforeAll - static void initShaderTransformer() throws Exception { - Reflect.setStatic(RenderSystem.class, "maxGlslVersion", 460); - ShaderTransformer.init(); + static void pinGlslVersion() { + previousMaxGlslVersion = TransformTestSupport.pinGlslVersion(460); + } + + @AfterAll + static void restoreGlslVersion() { + TransformTestSupport.restoreGlslVersion(previousMaxGlslVersion); } @BeforeEach void clearCaches() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); + TransformTestSupport.clearCaches(); } private static String patchedVertex() { diff --git a/src/test/java/net/coderbot/iris/pipeline/transform/LocalZeroInitTransformerTest.java b/src/test/java/net/coderbot/iris/pipeline/transform/LocalZeroInitTransformerTest.java index 756e53522..602265504 100644 --- a/src/test/java/net/coderbot/iris/pipeline/transform/LocalZeroInitTransformerTest.java +++ b/src/test/java/net/coderbot/iris/pipeline/transform/LocalZeroInitTransformerTest.java @@ -1,11 +1,12 @@ package net.coderbot.iris.pipeline.transform; -import com.gtnewhorizons.angelica.glsm.RenderSystem; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; import org.antlr.v4.runtime.tree.ParseTree; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.taumc.glsl.ShaderParser; import org.taumc.glsl.ShaderPrinter; import org.taumc.glsl.Transformer; @@ -15,6 +16,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -26,18 +28,13 @@ class LocalZeroInitTransformerTest { @BeforeAll static void initShaderTransformer() { - savedMaxGlsl = Reflect.getStatic(RenderSystem.class, "maxGlslVersion"); - Reflect.setStatic(RenderSystem.class, "maxGlslVersion", 460); ShaderTransformer.clearCache(); - ShaderTransformer.init(); + savedMaxGlsl = TransformTestSupport.pinGlslVersion(460); } @AfterAll static void restoreCapabilities() { - Reflect.setStatic(RenderSystem.class, "maxGlslVersion", savedMaxGlsl); - ShaderTransformer.clearCache(); - TransformPatcher.clearCache(); - ShaderTransformer.init(); + TransformTestSupport.restoreGlslVersion(savedMaxGlsl); } private static final Set NUMERIC = Set.of( @@ -129,34 +126,37 @@ class LocalZeroInitTransformerTest { assertTrue(out.contains("vec3(1.0)"), "b must keep its own initializer: " + out); } - @Test - void arrayDeclarationsAreSkippedInBothSpellings() { - final String src = - "#version 460 core\n" + - "void main() {\n" + - " float a[3];\n" + - " float[3] b;\n" + - " vec3 c[2];\n" + - "}\n"; - - final String out = run(src); - assertTrue(isUninitialized(out, "a"), out); - assertTrue(isUninitialized(out, "b"), out); - assertTrue(isUninitialized(out, "c"), out); + static Stream skippedLocals() { + return Stream.of( + Arguments.of("array declarations in both spellings", List.of("a", "b", "c"), + "#version 460 core\n" + + "void main() {\n" + + " float a[3];\n" + + " float[3] b;\n" + + " vec3 c[2];\n" + + "}\n"), + Arguments.of("struct locals have no scalar zero constructor", List.of("m"), + "#version 460 core\n" + + "struct Material { vec3 albedo; float rough; };\n" + + "void main() {\n" + + " Material m;\n" + + " m.rough = 1.0;\n" + + "}\n"), + Arguments.of("bool and double", List.of("flag", "d"), + "#version 460 core\n" + + "void main() {\n" + + " bool flag;\n" + + " double d;\n" + + "}\n")); } - @Test - void structAndOpaqueLocalsAreSkipped() { - final String src = - "#version 460 core\n" + - "struct Material { vec3 albedo; float rough; };\n" + - "void main() {\n" + - " Material m;\n" + - " m.rough = 1.0;\n" + - "}\n"; - + @ParameterizedTest(name = "{0}") + @MethodSource("skippedLocals") + void nonNumericLocalsAreSkipped(String name, List locals, String src) { final String out = run(src); - assertTrue(isUninitialized(out, "m"), "struct locals have no scalar zero constructor: " + out); + for (String local : locals) { + assertTrue(isUninitialized(out, local), local + " must stay uninitialized: " + out); + } } @Test @@ -173,20 +173,6 @@ class LocalZeroInitTransformerTest { assertTrue(out.contains("2.0"), "const initializer must survive: " + out); } - @Test - void boolAndDoubleAreSkipped() { - final String src = - "#version 460 core\n" + - "void main() {\n" + - " bool flag;\n" + - " double d;\n" + - "}\n"; - - final String out = run(src); - assertTrue(isUninitialized(out, "flag"), out); - assertTrue(isUninitialized(out, "d"), out); - } - private static int numericBareCount(String src) { return (int) uninitialized(src).stream().filter(d -> NUMERIC.contains(d.type())).count(); } diff --git a/src/test/java/net/coderbot/iris/pipeline/transform/RwImageStoreExtractorTest.java b/src/test/java/net/coderbot/iris/pipeline/transform/RwImageStoreExtractorTest.java index fe7f83e15..65285a1df 100644 --- a/src/test/java/net/coderbot/iris/pipeline/transform/RwImageStoreExtractorTest.java +++ b/src/test/java/net/coderbot/iris/pipeline/transform/RwImageStoreExtractorTest.java @@ -2,12 +2,18 @@ package net.coderbot.iris.pipeline.transform; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; +import java.util.stream.Stream; + +import static net.coderbot.iris.pipeline.transform.TransformTestSupport.norm; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class RwImageStoreExtractorTest { @@ -17,10 +23,6 @@ class RwImageStoreExtractorTest { RwImageStoreExtractor.setActiveCustomImages(null); } - private static String norm(String s) { - return s.replaceAll("\\s+", " ").trim(); - } - private static final String MINIMAL_VOX_VSH = String.join("\n", "#version 460 core", "#extension GL_ARB_shader_image_load_store : enable", @@ -71,65 +73,101 @@ class RwImageStoreExtractorTest { assertFalse(stripped.contains("imageAtomic"), stripped); } - @Test void detection_returnsNullWhenNoImageStore() { - assertNull(RwImageStoreExtractor.tryExtract(NO_IMAGESTORE_VSH, PatchShaderType.VERTEX, "shadow")); + static Stream undetected() { + return Stream.of(Arguments.of("no imageStore", NO_IMAGESTORE_VSH), Arguments.of("null input", null)); } - @Test void detection_returnsNullOnNullInput() { - assertNull(RwImageStoreExtractor.tryExtract(null, PatchShaderType.VERTEX, "shadow")); + @ParameterizedTest(name = "{0}") + @MethodSource("undetected") + void detection_returnsNull(String what, String source) { + assertNull(RwImageStoreExtractor.tryExtract(source, PatchShaderType.VERTEX, "shadow")); } - @Test void writtenImages_collectedFromImageStoreCalls() { + private static Arguments has(String what, String needle) { + return Arguments.of(what, false, needle, true); + } + + private static Arguments lacks(String what, String needle) { + return Arguments.of(what, false, needle, false); + } + + private static Arguments hasNorm(String what, String needle) { + return Arguments.of(what, true, needle, true); + } + + private static Arguments lacksNorm(String what, String needle) { + return Arguments.of(what, true, needle, false); + } + + private static void assertSource(String source, boolean normalized, String needle, boolean present) { + final String haystack = normalized ? norm(source) : source; + assertEquals(present, haystack.contains(needle), () -> (present ? "missing: " : "unexpected: ") + needle + "\n\n" + source); + } + + @Test void minimalVox_resultBasics() { final var result = RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow"); assertNotNull(result); assertTrue(result.writtenImages().contains("voxel_img")); - } - - @Test void modeIsChunkWhenChunkAttrsPresent() { - final var result = RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow"); assertEquals(RwImageStoreExtractor.RwExtractMode.CHUNK, result.mode()); - } - - @Test void compute_hasComputeVersionAndWorkgroup() { - final var result = RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow"); assertTrue(result.computeSource().startsWith("// _vg_mode: chunk\n")); - assertTrue(result.computeSource().contains("#version 460 core")); - assertTrue(result.computeSource().contains("layout(local_size_x = 64) in;")); } - @Test void compute_hasUniformsAndSsboBinding() { - final var result = RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow"); - final String s = result.computeSource(); - assertTrue(s.contains("uniform int _vg_rangeBase;")); - assertTrue(s.contains("uniform int _vg_rangeCount;")); - assertTrue(s.contains("uniform int _vg_vertexTotal;")); - assertTrue(s.contains("uniform int _vg_invocationBase;")); - assertTrue(s.contains("layout(std430, binding = 10) readonly buffer _VgRanges { uvec2 r[]; } _vg_ranges;")); - assertTrue(s.contains("int id = int(_vg_ranges.r[_vg_lo].x + (_vg_g - _vg_ranges.r[_vg_lo].y));")); - assertFalse(s.contains("_vg_startVertex")); - assertFalse(s.contains("_vg_vertexCount")); - assertTrue(s.contains("readonly buffer _VgVbuf")); - assertTrue(s.contains("layout(std430, binding = " + RwImageStoreExtractor.VG_VBUF_SSBO_BINDING + ") readonly buffer _VgVbuf"), "VBO SSBO must bind at the dedicated voxelization slot (not 0..8 which shaderpacks claim via buffer.N)"); + static Stream minimalVoxCompute() { + return Stream.of( + has("compute version", "#version 460 core"), + has("workgroup size", "layout(local_size_x = 64) in;"), + has("range base uniform", "uniform int _vg_rangeBase;"), + has("range count uniform", "uniform int _vg_rangeCount;"), + has("vertex total uniform", "uniform int _vg_vertexTotal;"), + has("invocation base uniform", "uniform int _vg_invocationBase;"), + has("ranges SSBO", "layout(std430, binding = 10) readonly buffer _VgRanges { uvec2 r[]; } _vg_ranges;"), + has("vertex id from the range table", "int id = int(_vg_ranges.r[_vg_lo].x + (_vg_g - _vg_ranges.r[_vg_lo].y));"), + lacks("no start vertex uniform", "_vg_startVertex"), + lacks("no vertex count uniform", "_vg_vertexCount"), + has("VBO SSBO binds at the dedicated voxelization slot, not 0..8 which shaderpacks claim via buffer.N", + "layout(std430, binding = " + RwImageStoreExtractor.VG_VBUF_SSBO_BINDING + ") readonly buffer _VgVbuf"), + has("original main renamed", "_vg_body"), + lacksNorm("user-original main no longer named main", "void main ( ) { texCoord ="), + has("synthesized main reads the invocation id", "gl_GlobalInvocationID"), + has("synthesized main unpacks", "_vg_unpack(uint(id));"), + has("synthesized main calls the body", "_vg_body();"), + has("unpack reads iris_Vertex", "iris_Vertex = vec4(uintBitsToFloat(_vg_vbuf.data[base + 0u])"), + has("unpack reads iris_Color", "iris_Color = vec4(float(_vg_vbuf.data[base + 3u] & 0xFFu)"), + has("ATTRIBUTES-patch mc_Entity is delivered as vec4 raw", "mc_Entity = vec4(float(_vg_vbuf.data[base + 10u])"), + lacksNorm("out qualifier stripped from varyings", "out vec2 texCoord"), + hasNorm("varying becomes a global", "vec2 texCoord"), + lacksNorm("flat is interface-only; a compute global carrying it does not compile", "flat"), + hasNorm("flat varying still becomes a global", "int mat"), + lacksNorm("iris_Vertex input declaration stripped", "in vec4 iris_Vertex"), + lacksNorm("mc_Entity attribute declaration stripped", "attribute vec4 mc_Entity")); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("minimalVoxCompute") + void minimalVox_compute(String what, boolean normalized, String needle, boolean present) { + assertSource(RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow").computeSource(), normalized, needle, present); + } + + static Stream minimalVoxRaster() { + return Stream.of( + lacks("no imageStore calls", "imageStore("), + lacks("no writeonly image decls", "writeonly uniform uimage"), + lacksNorm("no uimage3D voxel_img decl", "uniform uimage3D voxel_img"), + has("keeps main", "main"), + has("keeps a version directive", "#version"), + has("preserves the user #extension directive", "#extension GL_ARB_shader_image_load_store")); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("minimalVoxRaster") + void minimalVox_raster(String what, boolean normalized, String needle, boolean present) { + assertSource(RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow").strippedSource(), normalized, needle, present); } @Test void compute_ssboBindingDoesNotCollideWithShaderpackSlots() { assertTrue(RwImageStoreExtractor.VG_VBUF_SSBO_BINDING >= 9, "VG_VBUF_SSBO_BINDING must be >= 9 to avoid colliding with shaderpack buffer.N slots; got " + RwImageStoreExtractor.VG_VBUF_SSBO_BINDING); } - @Test void compute_renamesOriginalMainToVgBody() { - final var result = RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow"); - final String s = result.computeSource(); - assertTrue(s.contains("_vg_body"), "expected _vg_body in compute output"); - assertFalse(norm(s).contains("void main ( ) { texCoord ="), "user-original main must be renamed"); - } - - @Test void compute_synthesizedMainCallsUnpackAndBody() { - final String s = RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow").computeSource(); - assertTrue(s.contains("gl_GlobalInvocationID")); - assertTrue(s.contains("_vg_unpack(uint(id));")); - assertTrue(s.contains("_vg_body();")); - } - @Test void compute_hoistsAttributeDependentGlobalInitializers() { final String vsh = MINIMAL_VOX_VSH.replace( "void main() {", @@ -154,19 +192,6 @@ class RwImageStoreExtractorTest { assertTrue(n.indexOf("tintA =") == n.lastIndexOf("tintA ="), "tintA: initializer must not additionally be hoisted into the body"); } - @Test void compute_unpackHasExpectedAttributeReads() { - final String s = RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow").computeSource(); - assertTrue(s.contains("iris_Vertex = vec4(uintBitsToFloat(_vg_vbuf.data[base + 0u])")); - assertTrue(s.contains("iris_Color = vec4(float(_vg_vbuf.data[base + 3u] & 0xFFu)")); - assertTrue(s.contains("mc_Entity = vec4(float(_vg_vbuf.data[base + 10u])"), "ATTRIBUTES-patch mc_Entity is delivered as vec4 raw"); - } - - @Test void compute_dropsOutQualifierFromVaryings() { - final String s = RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow").computeSource(); - assertFalse(norm(s).contains("out vec2 texCoord"), "out qualifier should be stripped\n\n" + s); - assertTrue(norm(s).contains("vec2 texCoord"), "varying becomes a global"); - } - @Test void compute_vertInitOnlyAssignsUnpackedAttributes() { final String src = String.join("\n", "#version 460 core", @@ -183,12 +208,6 @@ class RwImageStoreExtractorTest { assertFalse(s.contains("a_LightCoord"), "an unused attribute must not be referenced\n\n" + s); } - @Test void compute_dropsInterpolationQualifierFromVaryings() { - final String s = norm(RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow").computeSource()); - assertFalse(s.contains("flat"), "flat is interface-only; a compute global carrying it does not compile\n\n" + s); - assertTrue(s.contains("int mat"), "the varying still becomes a global"); - } - @Test void compute_dropsAuxiliaryStorageQualifierFromVaryings() { final String src = String.join("\n", "#version 460 core", @@ -205,44 +224,16 @@ class RwImageStoreExtractorTest { assertTrue(s.contains("vec2 texCoord"), "the varying still becomes a global"); } - @Test void compute_stripsChunkVertexAttributeDeclarations() { - final String s = RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow").computeSource(); - assertFalse(norm(s).contains("in vec4 iris_Vertex")); - assertFalse(norm(s).contains("attribute vec4 mc_Entity")); - } - - @Test void raster_strippedHasNoImageStoreOrWriteonly() { - final var result = RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow"); - final String r = result.strippedSource(); - assertFalse(r.contains("imageStore("), "raster should have no imageStore calls"); - assertFalse(r.contains("writeonly uniform uimage"), "raster should have no writeonly image decls"); - assertFalse(norm(r).contains("uniform uimage3D voxel_img"), "raster should have no uimage3D voxel_img decl"); - assertTrue(r.contains("main"), "raster keeps main"); - } - - @Test void directives_preservedThroughRasterStrip() { - final var result = RwImageStoreExtractor.tryExtract(MINIMAL_VOX_VSH, PatchShaderType.VERTEX, "shadow"); - final String r = result.strippedSource(); - assertTrue(r.contains("#version"), "raster keeps a version directive"); - assertTrue(r.contains("#extension GL_ARB_shader_image_load_store"), "raster preserves user #extension directive\n\n" + r); - } - - @Test void knownAttributes_includesAllNineCanonicals() { - for (String name : new String[]{"iris_Vertex","iris_Color","iris_MultiTexCoord0","iris_MultiTexCoord1", "mc_midTexCoord","at_tangent","iris_Normal","mc_Entity","at_midBlock"}) { - assertNotNull(RwImageStoreExtractor.CHUNK_VERTEX_ATTRS.get(name), "missing " + name); - } - } - - @Test void knownAttributeVariants_includesCeleritasTypings() { - final var variants = RwImageStoreExtractor.CHUNK_VERTEX_ATTRS; - assertNotNull(variants.get("a_PosId").get("vec3")); - assertNotNull(variants.get("a_Color").get("vec4")); - assertNotNull(variants.get("a_TexCoord").get("vec2")); - assertNotNull(variants.get("a_LightCoord").get("uint")); - assertNotNull(variants.get("mc_Entity").get("vec4")); - assertNotNull(variants.get("mc_Entity").get("uint")); - assertNotNull(variants.get("mc_midTexCoord").get("vec4")); - assertNotNull(variants.get("mc_midTexCoord").get("vec2")); + @ParameterizedTest(name = "{0} {1}") + @CsvSource({ + "iris_Vertex,", "iris_Color,", "iris_MultiTexCoord0,", "iris_MultiTexCoord1,", "mc_midTexCoord,", "at_tangent,", "iris_Normal,", "mc_Entity,", "at_midBlock,", + "a_PosId, vec3", "a_Color, vec4", "a_TexCoord, vec2", "a_LightCoord, uint", + "mc_Entity, vec4", "mc_Entity, uint", "mc_midTexCoord, vec4", "mc_midTexCoord, vec2", + }) + void knownAttributes_includeCanonicalsAndCeleritasTypings(String name, String type) { + final var variants = RwImageStoreExtractor.CHUNK_VERTEX_ATTRS.get(name); + assertNotNull(variants, "missing " + name); + if (type != null) assertNotNull(variants.get(type), name + " has no " + type + " variant"); } private static final String CELERITAS_PATCHED_VOX_VSH = String.join("\n", @@ -302,31 +293,30 @@ class RwImageStoreExtractorTest { assertEquals(RwImageStoreExtractor.RwExtractMode.CHUNK, result.mode()); } - @Test void celeritas_computeContainsLiveBranchGlobals() { - final String s = RwImageStoreExtractor.tryExtract(CELERITAS_PATCHED_VOX_VSH, PatchShaderType.VERTEX, "CELERITAS_TERRAIN").computeSource(); - assertTrue(s.contains("vec3 a_PosId;"), "uncompressed (live) a_PosId global"); - assertTrue(s.contains("vec4 a_Color;")); - assertTrue(s.contains("vec2 a_TexCoord;")); - assertTrue(s.contains("uint a_LightCoord;")); - assertTrue(s.contains("uint mc_Entity;")); - assertTrue(s.contains("vec2 mc_midTexCoord;")); - assertTrue(s.contains("vec3 iris_Normal;")); - assertTrue(s.contains("vec4 at_midBlock;")); + static Stream celeritasCompute() { + return Stream.of( + has("uncompressed (live) a_PosId global", "vec3 a_PosId;"), + has("a_Color global", "vec4 a_Color;"), + has("a_TexCoord global", "vec2 a_TexCoord;"), + has("a_LightCoord global", "uint a_LightCoord;"), + has("mc_Entity global", "uint mc_Entity;"), + has("mc_midTexCoord global", "vec2 mc_midTexCoord;"), + has("iris_Normal global", "vec3 iris_Normal;"), + has("at_midBlock global", "vec4 at_midBlock;"), + has("unpack reads a_PosId", "a_PosId = vec3(uintBitsToFloat(_vg_vbuf.data[base + 0u])"), + has("unpack reads a_TexCoord", "a_TexCoord = vec2(uintBitsToFloat(_vg_vbuf.data[base + 4u])"), + has("unpack reads a_LightCoord", "a_LightCoord = _vg_vbuf.data[base + 6u]"), + has("celeritas-typed mc_Entity is delivered as the raw packed uint", "mc_Entity = _vg_vbuf.data[base + 10u]"), + has("mc_midTexCoord is an unnormalized ushort delivered raw by the vertex stage, so the compute prelude must not rescale it", + "mc_midTexCoord = vec2(float(_vg_vbuf.data[base + 7u] & 0xFFFFu),"), + has("vertex decode assigns position", "_vert_position = a_PosId;"), + has("_get_draw_translation depends on _draw_id", "_draw_id = (_vg_draw_params >> 8) & 0xFFu;")); } - @Test void celeritas_unpackContainsCeleritasFormulas() { - final String s = RwImageStoreExtractor.tryExtract(CELERITAS_PATCHED_VOX_VSH, PatchShaderType.VERTEX, "CELERITAS_TERRAIN").computeSource(); - assertTrue(s.contains("a_PosId = vec3(uintBitsToFloat(_vg_vbuf.data[base + 0u])")); - assertTrue(s.contains("a_TexCoord = vec2(uintBitsToFloat(_vg_vbuf.data[base + 4u])")); - assertTrue(s.contains("a_LightCoord = _vg_vbuf.data[base + 6u]")); - assertTrue(s.contains("mc_Entity = _vg_vbuf.data[base + 10u]"), "celeritas-typed mc_Entity must be delivered as the raw packed uint"); - assertTrue(s.contains("mc_midTexCoord = vec2(float(_vg_vbuf.data[base + 7u] & 0xFFFFu),"), "mc_midTexCoord is an unnormalized ushort delivered raw by the vertex stage, so the compute prelude must not rescale it"); - } - - @Test void celeritas_computeDecodesVertexGlobals() { - final String s = RwImageStoreExtractor.tryExtract(CELERITAS_PATCHED_VOX_VSH, PatchShaderType.VERTEX, "CELERITAS_TERRAIN").computeSource(); - assertTrue(s.contains("_vert_position = a_PosId;"), "vertex decode must assign position\n\n" + s); - assertTrue(s.contains("_draw_id = (_vg_draw_params >> 8) & 0xFFu;"), "_get_draw_translation depends on _draw_id\n\n" + s); + @ParameterizedTest(name = "{0}") + @MethodSource("celeritasCompute") + void celeritas_compute(String what, boolean normalized, String needle, boolean present) { + assertSource(RwImageStoreExtractor.tryExtract(CELERITAS_PATCHED_VOX_VSH, PatchShaderType.VERTEX, "CELERITAS_TERRAIN").computeSource(), normalized, needle, present); } @Test void celeritas_vertexDecodeComesAfterTheGlobalDeclarations() { @@ -339,13 +329,6 @@ class RwImageStoreExtractorTest { } } - @Test void celeritas_strippedHasNoImageStoreOrUimage() { - final var result = RwImageStoreExtractor.tryExtract(CELERITAS_PATCHED_VOX_VSH, PatchShaderType.VERTEX, "CELERITAS_TERRAIN"); - final String r = result.strippedSource(); - assertFalse(r.contains("imageStore(")); - assertFalse(norm(r).contains("uniform uimage3D voxel_img")); - } - private static final String MULTI_IMAGE_VSH = String.join("\n", "#version 460 core", "in vec4 iris_Vertex;", @@ -612,13 +595,6 @@ class RwImageStoreExtractorTest { assertTrue(nc.contains("ivec4 ( _vg_prev +"), "inline emulation must compute prev + val in the destination vec\n\n" + c); } - @Test void atomicEmulation_droppedWriteonlyQualifier() { - final var result = RwImageStoreExtractor.tryExtract(ATOMIC_NOT_WRITEONLY_VSH, PatchShaderType.VERTEX, "atomic"); - final String c = result.computeSource(); - assertFalse(c.contains("writeonly uniform iimage2D endcrystal_img"), "image used by imageAtomic* must not get writeonly qualifier (now load+store)\n\n" + c); - assertTrue(c.contains("uniform iimage2D endcrystal_img"), "image decl present in prelude\n\n" + c); - } - private static final String MIXED_ATOMIC_AND_STORE_VSH = String.join("\n", "#version 460 core", "in vec4 iris_Vertex;", diff --git a/src/test/java/net/coderbot/iris/pipeline/transform/SamplerAliasDeduplicatorTest.java b/src/test/java/net/coderbot/iris/pipeline/transform/SamplerAliasDeduplicatorTest.java index 93ce8aaf6..e4015d92f 100644 --- a/src/test/java/net/coderbot/iris/pipeline/transform/SamplerAliasDeduplicatorTest.java +++ b/src/test/java/net/coderbot/iris/pipeline/transform/SamplerAliasDeduplicatorTest.java @@ -1,27 +1,26 @@ package net.coderbot.iris.pipeline.transform; import org.junit.jupiter.api.Test; -import org.taumc.glsl.ShaderParser; -import org.taumc.glsl.ShaderPrinter; import org.taumc.glsl.Transformer; import java.util.EnumMap; import java.util.Map; +import static net.coderbot.iris.pipeline.transform.TransformTestSupport.format; +import static net.coderbot.iris.pipeline.transform.TransformTestSupport.occurrences; +import static net.coderbot.iris.pipeline.transform.TransformTestSupport.parse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class SamplerAliasDeduplicatorTest { - private static Transformer parse(String src) { - return new Transformer(ShaderParser.parseShader(src).full()); - } - - private static String format(Transformer t) { - StringBuilder sb = new StringBuilder(); - t.mutateTree(tree -> sb.append(ShaderPrinter.getFormattedShader(tree))); - return sb.toString(); + private static String transformFragment(String src) { + final Transformer t = parse(src); + final Map trees = new EnumMap<>(PatchShaderType.class); + trees.put(PatchShaderType.FRAGMENT, t); + SamplerAliasDeduplicator.transformGrouped(trees, null); + return format(t); } @Test @@ -35,12 +34,7 @@ class SamplerAliasDeduplicatorTest { " fragColor = texture(textureAtlas, vec2(0.5)) + texture(tex, vec2(0.5));\n" + "}\n"; - Transformer t = parse(src); - Map trees = new EnumMap<>(PatchShaderType.class); - trees.put(PatchShaderType.FRAGMENT, t); - SamplerAliasDeduplicator.transformGrouped(trees, null); - - String out = format(t); + String out = transformFragment(src); assertTrue(out.contains("uniform sampler2D tex"), "canonical declaration must remain"); assertFalse(out.contains("uniform sampler2D textureAtlas"), "alias declaration must be removed: " + out); assertFalse(out.contains("textureAtlas"), "all references to alias must be rewritten: " + out); @@ -55,12 +49,7 @@ class SamplerAliasDeduplicatorTest { "out vec4 fragColor;\n" + "void main() { fragColor = texture(textureAtlas, vec2(0.5)); }\n"; - Transformer t = parse(src); - Map trees = new EnumMap<>(PatchShaderType.class); - trees.put(PatchShaderType.FRAGMENT, t); - SamplerAliasDeduplicator.transformGrouped(trees, null); - - String out = format(t); + String out = transformFragment(src); assertTrue(out.contains("uniform sampler2D tex"), "alias should be promoted to canonical: " + out); assertFalse(out.contains("textureAtlas"), "no alias should remain: " + out); } @@ -103,12 +92,7 @@ class SamplerAliasDeduplicatorTest { "out vec4 fragColor;\n" + "void main() { fragColor = texture(somethingElse, vec2(0.5)); }\n"; - Transformer t = parse(src); - Map trees = new EnumMap<>(PatchShaderType.class); - trees.put(PatchShaderType.FRAGMENT, t); - SamplerAliasDeduplicator.transformGrouped(trees, null); - - String out = format(t); + String out = transformFragment(src); assertTrue(out.contains("uniform sampler2D somethingElse"), "untouched uniform must survive: " + out); assertFalse(out.contains("uniform sampler2D tex"), "no spurious tex declaration: " + out); } @@ -122,19 +106,8 @@ class SamplerAliasDeduplicatorTest { "out vec4 fragColor;\n" + "void main() { fragColor = texture(gdepthtex, vec2(0.5)) + texture(depthtex0, vec2(0.5)); }\n"; - Transformer t = parse(src); - Map trees = new EnumMap<>(PatchShaderType.class); - trees.put(PatchShaderType.FRAGMENT, t); - SamplerAliasDeduplicator.transformGrouped(trees, null); - - String out = format(t); + String out = transformFragment(src); assertTrue(out.contains("uniform sampler2D depthtex0")); assertFalse(out.contains("gdepthtex"), out); } - - private static int occurrences(String haystack, String needle) { - int n = 0, i = 0; - while ((i = haystack.indexOf(needle, i)) != -1) { n++; i += needle.length(); } - return n; - } } diff --git a/src/test/java/net/coderbot/iris/pipeline/transform/SamplerToStorageImageRewriterTest.java b/src/test/java/net/coderbot/iris/pipeline/transform/SamplerToStorageImageRewriterTest.java index d0acc4029..1237277a5 100644 --- a/src/test/java/net/coderbot/iris/pipeline/transform/SamplerToStorageImageRewriterTest.java +++ b/src/test/java/net/coderbot/iris/pipeline/transform/SamplerToStorageImageRewriterTest.java @@ -9,15 +9,20 @@ import net.coderbot.iris.gl.image.ImageInformation; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.taumc.glsl.ShaderParser; -import org.taumc.glsl.ShaderPrinter; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.taumc.glsl.Transformer; import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Stream; +import static net.coderbot.iris.pipeline.transform.TransformTestSupport.format; +import static net.coderbot.iris.pipeline.transform.TransformTestSupport.occurrences; +import static net.coderbot.iris.pipeline.transform.TransformTestSupport.parse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -34,14 +39,12 @@ class SamplerToStorageImageRewriterTest { SamplerToStorageImageRewriter.setActiveCandidates(Set.of()); } - private static Transformer parse(String src) { - return new Transformer(ShaderParser.parseShader(src).full()); - } - - private static String format(Transformer t) { - StringBuilder sb = new StringBuilder(); - t.mutateTree(tree -> sb.append(ShaderPrinter.getFormattedShader(tree))); - return sb.toString(); + private static String transformFragment(String src) { + final Transformer t = parse(src); + final Map trees = new EnumMap<>(PatchShaderType.class); + trees.put(PatchShaderType.FRAGMENT, t); + SamplerToStorageImageRewriter.transformGrouped(trees, null); + return format(t); } @Test @@ -59,12 +62,7 @@ class SamplerToStorageImageRewriterTest { " fragColor = vec4(float(v));\n" + "}\n"; - Transformer t = parse(src); - Map trees = new EnumMap<>(PatchShaderType.class); - trees.put(PatchShaderType.FRAGMENT, t); - SamplerToStorageImageRewriter.transformGrouped(trees, null); - - String out = format(t); + String out = transformFragment(src); assertFalse(out.contains("usampler3D wsr_sampler"), "old sampler declaration must be gone: " + out); assertTrue(out.contains("uimage3D wsr_img"), "image declaration must be injected: " + out); assertTrue(out.contains("readonly"), "image must be marked readonly: " + out); @@ -74,99 +72,67 @@ class SamplerToStorageImageRewriterTest { assertFalse(out.contains("wsr_sampler"), "no reference to the old sampler name: " + out); } - @Test - void filteredRead_abortsRewrite() { - SamplerToStorageImageRewriter.setActiveCandidates(Set.of( - new SamplerToStorageImageRewriter.Candidate("ff_sampler", "ff_img", "r16ui", 3, false) - )); - - String src = - "#version 460 core\n" + - "uniform usampler3D ff_sampler;\n" + - "out vec4 fragColor;\n" + - "void main() {\n" + - " uvec4 v = texture(ff_sampler, vec3(0.5, 0.5, 0.5));\n" + - " fragColor = vec4(float(v.r));\n" + - "}\n"; - - Transformer t = parse(src); - Map trees = new EnumMap<>(PatchShaderType.class); - trees.put(PatchShaderType.FRAGMENT, t); - SamplerToStorageImageRewriter.transformGrouped(trees, null); - - String out = format(t); - assertTrue(out.contains("usampler3D ff_sampler"), "sampler must remain when filtered read present: " + out); - assertFalse(out.contains("uimage3D"), "no image injected: " + out); + static Stream abortingReads() { + return Stream.of( + Arguments.of("filtered read", new SamplerToStorageImageRewriter.Candidate("ff_sampler", "ff_img", "r16ui", 3, false), + "#version 460 core\n" + + "uniform usampler3D ff_sampler;\n" + + "out vec4 fragColor;\n" + + "void main() {\n" + + " uvec4 v = texture(ff_sampler, vec3(0.5, 0.5, 0.5));\n" + + " fragColor = vec4(float(v.r));\n" + + "}\n", + "usampler3D ff_sampler", "uimage3D"), + Arguments.of("legacy texture2D read", new SamplerToStorageImageRewriter.Candidate("puddle_sampler", "puddle_img", "r8ui", 2, false), + "#version 460 core\n" + + "uniform usampler2D puddle_sampler;\n" + + "out vec4 fragColor;\n" + + "void main() {\n" + + " float noPuddles = texture2D(puddle_sampler, vec2(0.5)).r;\n" + + " fragColor = vec4(noPuddles);\n" + + "}\n", + "usampler2D puddle_sampler", "puddle_img"), + Arguments.of("legacy texture3D read", new SamplerToStorageImageRewriter.Candidate("voxel_sampler", "voxel_img", "r16ui", 3, false), + "#version 460 core\n" + + "uniform usampler3D voxel_sampler;\n" + + "out vec4 fragColor;\n" + + "void main() {\n" + + " fragColor = vec4(float(texture3D(voxel_sampler, vec3(0.5)).r));\n" + + "}\n", + "usampler3D voxel_sampler", "voxel_img")); } - @Test - void legacyTexture2DRead_abortsRewrite() { - SamplerToStorageImageRewriter.setActiveCandidates(Set.of( - new SamplerToStorageImageRewriter.Candidate("puddle_sampler", "puddle_img", "r8ui", 2, false) - )); + @ParameterizedTest(name = "{0}") + @MethodSource("abortingReads") + void nonTexelFetchRead_abortsRewrite(String name, SamplerToStorageImageRewriter.Candidate candidate, String src, String samplerDecl, String injected) { + SamplerToStorageImageRewriter.setActiveCandidates(Set.of(candidate)); - String src = - "#version 460 core\n" + - "uniform usampler2D puddle_sampler;\n" + - "out vec4 fragColor;\n" + - "void main() {\n" + - " float noPuddles = texture2D(puddle_sampler, vec2(0.5)).r;\n" + - " fragColor = vec4(noPuddles);\n" + - "}\n"; - - Transformer t = parse(src); - Map trees = new EnumMap<>(PatchShaderType.class); - trees.put(PatchShaderType.FRAGMENT, t); - SamplerToStorageImageRewriter.transformGrouped(trees, null); - - String out = format(t); - assertTrue(out.contains("usampler2D puddle_sampler"), "sampler must remain when read with texture2D: " + out); - assertFalse(out.contains("puddle_img"), "no image may be injected: " + out); + String out = transformFragment(src); + assertTrue(out.contains(samplerDecl), "sampler must remain: " + out); + assertFalse(out.contains(injected), "no image may be injected: " + out); } - @Test - void legacyTexture3DRead_abortsRewrite() { - SamplerToStorageImageRewriter.setActiveCandidates(Set.of( - new SamplerToStorageImageRewriter.Candidate("voxel_sampler", "voxel_img", "r16ui", 3, false) - )); - - String src = - "#version 460 core\n" + - "uniform usampler3D voxel_sampler;\n" + - "out vec4 fragColor;\n" + - "void main() {\n" + - " fragColor = vec4(float(texture3D(voxel_sampler, vec3(0.5)).r));\n" + - "}\n"; - - Transformer t = parse(src); - Map trees = new EnumMap<>(PatchShaderType.class); - trees.put(PatchShaderType.FRAGMENT, t); - SamplerToStorageImageRewriter.transformGrouped(trees, null); - - String out = format(t); - assertTrue(out.contains("usampler3D voxel_sampler"), "sampler must remain: " + out); - assertFalse(out.contains("voxel_img"), "no image may be injected: " + out); + static Stream nonCandidates() { + return Stream.of( + Arguments.of("float sampler", "sampler3D ff_sampler", + "#version 460 core\n" + + "uniform sampler3D ff_sampler;\n" + + "out vec4 fragColor;\n" + + "void main() {\n" + + " fragColor = texelFetch(ff_sampler, ivec3(0), 0);\n" + + "}\n"), + Arguments.of("integer sampler", "usampler3D wsr_sampler", + "#version 460 core\n" + + "uniform usampler3D wsr_sampler;\n" + + "out vec4 fragColor;\n" + + "void main() { fragColor = vec4(float(texelFetch(wsr_sampler, ivec3(0), 0).r)); }\n")); } - @Test - void floatSamplerNotInCandidateSet_unchanged() { - SamplerToStorageImageRewriter.setActiveCandidates(Set.of()); - - String src = - "#version 460 core\n" + - "uniform sampler3D ff_sampler;\n" + - "out vec4 fragColor;\n" + - "void main() {\n" + - " fragColor = texelFetch(ff_sampler, ivec3(0), 0);\n" + - "}\n"; - - Transformer t = parse(src); - Map trees = new EnumMap<>(PatchShaderType.class); - trees.put(PatchShaderType.FRAGMENT, t); - SamplerToStorageImageRewriter.transformGrouped(trees, null); - - String out = format(t); - assertTrue(out.contains("sampler3D ff_sampler"), "float sampler must stay sampler: " + out); + @ParameterizedTest(name = "{0}") + @MethodSource("nonCandidates") + void noActiveCandidates_unchanged(String name, String samplerDecl, String src) { + String out = transformFragment(src); + assertTrue(out.contains(samplerDecl), "sampler must remain when no candidates: " + out); assertFalse(out.contains("uimage3D"), out); } @@ -227,12 +193,7 @@ class SamplerToStorageImageRewriterTest { " fragColor = vec4(float(a + b + c));\n" + "}\n"; - Transformer t = parse(src); - Map trees = new EnumMap<>(PatchShaderType.class); - trees.put(PatchShaderType.FRAGMENT, t); - SamplerToStorageImageRewriter.transformGrouped(trees, null); - - String out = format(t); + String out = transformFragment(src); assertFalse(out.contains("usampler3D"), "no sampler declarations left: " + out); assertTrue(out.contains("uimage3D wsr_img"), out); assertTrue(out.contains("uimage3D wsr_lod_img"), out); @@ -259,12 +220,7 @@ class SamplerToStorageImageRewriterTest { " fragColor = vec4(float(v.r));\n" + "}\n"; - Transformer t = parse(src); - Map trees = new EnumMap<>(PatchShaderType.class); - trees.put(PatchShaderType.FRAGMENT, t); - SamplerToStorageImageRewriter.transformGrouped(trees, null); - - String out = format(t); + String out = transformFragment(src); assertTrue(out.contains("usampler3D wsr_sampler"), "sampler decl must remain: " + out); assertTrue(out.contains("writeonly"), "writeonly image decl must remain: " + out); assertEquals(1, occurrences(out, "uniform uimage3D wsr_img"), "exactly one uimage3D wsr_img declaration (no second readonly injected): " + out); @@ -273,30 +229,6 @@ class SamplerToStorageImageRewriterTest { assertTrue(out.contains("imageStore(wsr_img"), "imageStore on writer must survive: " + out); } - private static int occurrences(String haystack, String needle) { - int n = 0, i = 0; - while ((i = haystack.indexOf(needle, i)) != -1) { n++; i += needle.length(); } - return n; - } - - @Test - void noActiveCandidates_noOp() { - String src = - "#version 460 core\n" + - "uniform usampler3D wsr_sampler;\n" + - "out vec4 fragColor;\n" + - "void main() { fragColor = vec4(float(texelFetch(wsr_sampler, ivec3(0), 0).r)); }\n"; - - Transformer t = parse(src); - Map trees = new EnumMap<>(PatchShaderType.class); - trees.put(PatchShaderType.FRAGMENT, t); - SamplerToStorageImageRewriter.transformGrouped(trees, null); - - String out = format(t); - assertTrue(out.contains("usampler3D wsr_sampler"), "sampler must remain when no candidates: " + out); - assertFalse(out.contains("uimage3D"), out); - } - @Test void integerCustomImages_produceNoCandidates_onNonSdlBackend() { assertFalse(BackendManager.RENDER_BACKEND.isSDLGPU(), "test backend must not be SDL-GPU for this to mean anything"); diff --git a/src/test/java/net/coderbot/iris/pipeline/transform/ShaderTransformerDiskCacheTest.java b/src/test/java/net/coderbot/iris/pipeline/transform/ShaderTransformerDiskCacheTest.java index 3046e0e8d..6713a456b 100644 --- a/src/test/java/net/coderbot/iris/pipeline/transform/ShaderTransformerDiskCacheTest.java +++ b/src/test/java/net/coderbot/iris/pipeline/transform/ShaderTransformerDiskCacheTest.java @@ -40,21 +40,15 @@ class ShaderTransformerDiskCacheTest { @BeforeEach void setUp() throws Exception { - savedMaxGlsl = Reflect.getStatic(RenderSystem.class, "maxGlslVersion"); - Reflect.setStatic(RenderSystem.class, "maxGlslVersion", 460); - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); - ShaderTransformer.init(); + TransformTestSupport.clearCaches(); + savedMaxGlsl = TransformTestSupport.pinGlslVersion(460); ShaderDiskCache.configure(dir, "t"); } @AfterEach void tearDown() throws Exception { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); Reflect.setStatic(ShaderDiskCache.class, "root", null); - Reflect.setStatic(RenderSystem.class, "maxGlslVersion", savedMaxGlsl); - ShaderTransformer.init(); + TransformTestSupport.restoreGlslVersion(savedMaxGlsl); } private static EnumMap compositeInputs() { @@ -74,8 +68,7 @@ class ShaderTransformerDiskCacheTest { @Test void compositeDiskHitSkipsTransform() { assertNotNull(TransformPatcher.patchComposite(VERTEX, null, FRAGMENT)); - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); + TransformTestSupport.clearCaches(); ShaderDiskCache.putStrings(ShaderTransformer.diskKey(Patch.COMPOSITE, compositeInputs(), compositeParams()), Map.of("VERTEX", "POISON-V", "FRAGMENT", "POISON-F")); @@ -87,8 +80,7 @@ class ShaderTransformerDiskCacheTest { @Test void computeDiskHitSkipsTransform() { assertNotNull(TransformPatcher.patchCompute("shadowcomp", COMPUTE, null, null)); - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); + TransformTestSupport.clearCaches(); final EnumMap inputs = new EnumMap<>(PatchShaderType.class); inputs.put(PatchShaderType.COMPUTE, COMPUTE); diff --git a/src/test/java/net/coderbot/iris/pipeline/transform/ShaderTransformerTest.java b/src/test/java/net/coderbot/iris/pipeline/transform/ShaderTransformerTest.java index 7050ef4ba..cb05dc4e0 100644 --- a/src/test/java/net/coderbot/iris/pipeline/transform/ShaderTransformerTest.java +++ b/src/test/java/net/coderbot/iris/pipeline/transform/ShaderTransformerTest.java @@ -7,7 +7,9 @@ import com.gtnewhorizons.angelica.glsm.backend.BackendManager; import net.coderbot.iris.pipeline.transform.parameter.TextureStageParameters; import net.coderbot.iris.shaderpack.texture.TextureStage; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.taumc.glsl.ShaderParser; @@ -33,32 +35,30 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; class ShaderTransformerTest { private static boolean savedImageLoadStore; - - private static void setImageLoadStore(boolean value) throws Exception { - Reflect.setStatic(RenderSystem.class, "supportsImageLoadStore", value); - } - - private static boolean getImageLoadStore() throws Exception { - return Reflect.getStatic(RenderSystem.class, "supportsImageLoadStore"); - } + private static int savedMaxGlsl; @BeforeAll - static void initShaderTransformer() throws Exception { - Reflect.setStatic(RenderSystem.class, "maxGlslVersion", 460); - - savedImageLoadStore = getImageLoadStore(); - setImageLoadStore(true); - + static void initShaderTransformer() { + savedImageLoadStore = Reflect.getStatic(RenderSystem.class, "supportsImageLoadStore"); + Reflect.setStatic(RenderSystem.class, "supportsImageLoadStore", true); ShaderTransformer.clearCache(); - ShaderTransformer.init(); + savedMaxGlsl = TransformTestSupport.pinGlslVersion(460); } @AfterAll - static void restoreCapabilities() throws Exception { - setImageLoadStore(savedImageLoadStore); - ShaderTransformer.clearCache(); - TransformPatcher.clearCache(); - ShaderTransformer.init(); + static void restoreCapabilities() { + Reflect.setStatic(RenderSystem.class, "supportsImageLoadStore", savedImageLoadStore); + TransformTestSupport.restoreGlslVersion(savedMaxGlsl); + } + + @BeforeEach + void clearCaches() { + TransformTestSupport.clearCaches(); + } + + @AfterEach + void clearCandidates() { + SamplerToStorageImageRewriter.setActiveCandidates(Set.of()); } private static int versionOf(String shader) { @@ -69,61 +69,47 @@ class ShaderTransformerTest { @Test void storageImageRewrite_hoistsEmittedVersion() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); SamplerToStorageImageRewriter.setActiveCandidates(Set.of(new SamplerToStorageImageRewriter.Candidate("voxel_sampler", "voxel_img", "r16ui", 3, false))); - try { - final String vertex = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }"; - final String fragment = String.join("\n", - "#version 330 core", - "uniform usampler3D voxel_sampler;", - "void main(){ gl_FragColor = vec4(float(texelFetch(voxel_sampler, ivec3(0), 0).r)); }", - ""); + final String vertex = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }"; + final String fragment = String.join("\n", + "#version 330 core", + "uniform usampler3D voxel_sampler;", + "void main(){ gl_FragColor = vec4(float(texelFetch(voxel_sampler, ivec3(0), 0).r)); }", + ""); - final Map out = TransformPatcher.patchComposite(vertex, null, fragment); - assertNotNull(out); - final String fsh = out.get(PatchShaderType.FRAGMENT); - final String vsh = out.get(PatchShaderType.VERTEX); + final Map out = TransformPatcher.patchComposite(vertex, null, fragment); + assertNotNull(out); + final String fsh = out.get(PatchShaderType.FRAGMENT); + final String vsh = out.get(PatchShaderType.VERTEX); - assertTrue(fsh.contains("imageLoad"), "fixture must actually exercise the rewrite\n\n" + fsh); - assertFalse(fsh.contains("usampler3D voxel_sampler"), fsh); + assertTrue(fsh.contains("imageLoad"), "fixture must actually exercise the rewrite\n\n" + fsh); + assertFalse(fsh.contains("usampler3D voxel_sampler"), fsh); - assertTrue(versionOf(fsh) >= 420, "injected imageLoad needs GLSL 420\n\n" + fsh); - assertEquals(330, versionOf(vsh), "the vertex declares no candidate sampler and must not be inflated\n\n" + vsh); - } finally { - SamplerToStorageImageRewriter.setActiveCandidates(Set.of()); - } + assertTrue(versionOf(fsh) >= 420, "injected imageLoad needs GLSL 420\n\n" + fsh); + assertEquals(330, versionOf(vsh), "the vertex declares no candidate sampler and must not be inflated\n\n" + vsh); } @Test void filteredRead_doesNotHoistVersion() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); SamplerToStorageImageRewriter.setActiveCandidates(Set.of(new SamplerToStorageImageRewriter.Candidate("voxel_sampler", "voxel_img", "r16ui", 3, false))); - try { - final String vertex = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }"; - final String fragment = String.join("\n", - "#version 330 core", - "uniform usampler3D voxel_sampler;", - "void main(){ gl_FragColor = vec4(float(texture(voxel_sampler, vec3(0.5)).r)); }", - ""); + final String vertex = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }"; + final String fragment = String.join("\n", + "#version 330 core", + "uniform usampler3D voxel_sampler;", + "void main(){ gl_FragColor = vec4(float(texture(voxel_sampler, vec3(0.5)).r)); }", + ""); - final Map out = TransformPatcher.patchComposite(vertex, null, fragment); - assertNotNull(out); - final String fsh = out.get(PatchShaderType.FRAGMENT); + final Map out = TransformPatcher.patchComposite(vertex, null, fragment); + assertNotNull(out); + final String fsh = out.get(PatchShaderType.FRAGMENT); - assertTrue(fsh.contains("usampler3D voxel_sampler"), "rewrite must abort on a filtered read\n\n" + fsh); - assertFalse(fsh.contains("imageLoad"), fsh); - assertEquals(330, versionOf(fsh), fsh); - } finally { - SamplerToStorageImageRewriter.setActiveCandidates(Set.of()); - } + assertTrue(fsh.contains("usampler3D voxel_sampler"), "rewrite must abort on a filtered read\n\n" + fsh); + assertFalse(fsh.contains("imageLoad"), fsh); + assertEquals(330, versionOf(fsh), fsh); } @Test void noCandidates_versionUnchanged() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); final String vertex = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }"; final String fragment = "#version 330 core\nvoid main(){ gl_FragColor = vec4(1.0); }"; @@ -135,31 +121,23 @@ class ShaderTransformerTest { @Test void computeStorageImageRewrite_hoistsEmittedVersion() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); SamplerToStorageImageRewriter.setActiveCandidates(Set.of(new SamplerToStorageImageRewriter.Candidate("voxel_sampler", "voxel_img", "r16ui", 3, false))); - try { - final String compute = String.join("\n", - "#version 330 core", - "layout(local_size_x = 8) in;", - "uniform usampler3D voxel_sampler;", - "out vec4 unused;", - "void main(){ unused = vec4(float(texelFetch(voxel_sampler, ivec3(gl_GlobalInvocationID), 0).x)); }", - ""); + final String compute = String.join("\n", + "#version 330 core", + "layout(local_size_x = 8) in;", + "uniform usampler3D voxel_sampler;", + "out vec4 unused;", + "void main(){ unused = vec4(float(texelFetch(voxel_sampler, ivec3(gl_GlobalInvocationID), 0).x)); }", + ""); - final String out = TransformPatcher.patchCompute("shadowcomp", compute, null, null); - assertNotNull(out); - assertTrue(out.contains("imageLoad"), "fixture must actually exercise the rewrite\n\n" + out); - assertTrue(versionOf(out) >= 420, "injected imageLoad needs GLSL 420\n\n" + out); - } finally { - SamplerToStorageImageRewriter.setActiveCandidates(Set.of()); - } + final String out = TransformPatcher.patchCompute("shadowcomp", compute, null, null); + assertNotNull(out); + assertTrue(out.contains("imageLoad"), "fixture must actually exercise the rewrite\n\n" + out); + assertTrue(versionOf(out) >= 420, "injected imageLoad needs GLSL 420\n\n" + out); } @Test void packDeclaredImageTypeHoistsWithoutImageLoad() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); final String vertex = "#version 330 core\nvoid main(){ gl_Position = vec4(0.0); }"; final String fragment = String.join("\n", "#version 330 core", @@ -175,39 +153,30 @@ class ShaderTransformerTest { @Test void computeIntegerSamplerBecomesStorageImage() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); SamplerToStorageImageRewriter.setActiveCandidates(Set.of( new SamplerToStorageImageRewriter.Candidate("voxel_sampler", "voxel_img", "r16ui", 3, false))); - try { - final String compute = String.join("\n", - "#version 460 core", - "layout(local_size_x = 8) in;", - "uniform usampler3D voxel_sampler;", - "layout(r16ui) writeonly uniform uimage3D floodfill_img;", - "void main() {", - " uint v = texelFetch(voxel_sampler, ivec3(gl_GlobalInvocationID), 0).x;", - " imageStore(floodfill_img, ivec3(gl_GlobalInvocationID), uvec4(v, 0u, 0u, 0u));", - "}", - ""); - final String out = TransformPatcher.patchCompute("shadowcomp", compute, null, null); - assertNotNull(out); - final String norm = out.replaceAll("\\s+", " "); - assertFalse(norm.contains("usampler3D voxel_sampler"), - "an integer sampler cannot be bound on SDL_GPU and must become a storage image\n\n" + out); - assertTrue(norm.contains("uimage3D voxel_img"), "expected the rewritten storage image\n\n" + out); - assertTrue(norm.contains("imageLoad ( voxel_img"), "texelFetch must become imageLoad\n\n" + out); - } finally { - SamplerToStorageImageRewriter.setActiveCandidates(Set.of()); - } + final String compute = String.join("\n", + "#version 460 core", + "layout(local_size_x = 8) in;", + "uniform usampler3D voxel_sampler;", + "layout(r16ui) writeonly uniform uimage3D floodfill_img;", + "void main() {", + " uint v = texelFetch(voxel_sampler, ivec3(gl_GlobalInvocationID), 0).x;", + " imageStore(floodfill_img, ivec3(gl_GlobalInvocationID), uvec4(v, 0u, 0u, 0u));", + "}", + ""); + final String out = TransformPatcher.patchCompute("shadowcomp", compute, null, null); + assertNotNull(out); + final String norm = TransformTestSupport.norm(out); + assertFalse(norm.contains("usampler3D voxel_sampler"), + "an integer sampler cannot be bound on SDL_GPU and must become a storage image\n\n" + out); + assertTrue(norm.contains("uimage3D voxel_img"), "expected the rewritten storage image\n\n" + out); + assertTrue(norm.contains("imageLoad ( voxel_img"), "texelFetch must become imageLoad\n\n" + out); } @Test void samplerAliasesSurvive_onNonSdlBackend() { assertFalse(BackendManager.RENDER_BACKEND.isSDLGPU(), "test backend must not be SDL-GPU for this to mean anything"); - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); - final String vertex = "#version 330 core\nvoid main(){gl_Position = vec4(0.0);}"; final String fragment = String.join("\n", "#version 330 core", @@ -237,8 +206,6 @@ class ShaderTransformerTest { @Test void extractedStageDropsItsArtifact() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); final EnumMap artifacts = new EnumMap<>(PatchShaderType.class); final Map out = ShaderTransformer.transform(PLAIN_VERTEX, null, null, null, IMAGE_STORE_FRAGMENT, new TextureStageParameters(Patch.COMPOSITE, TextureStage.COMPOSITE_AND_FINAL, null), artifacts); assertNotNull(out); @@ -256,8 +223,6 @@ class ShaderTransformerTest { @Test void survivingArtifactsMatchTheirSource() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); final EnumMap artifacts = new EnumMap<>(PatchShaderType.class); final Map out = ShaderTransformer.transform(PLAIN_VERTEX, null, null, null, IMAGE_STORE_FRAGMENT, new TextureStageParameters(Patch.COMPOSITE, TextureStage.COMPOSITE_AND_FINAL, null), artifacts); assertNotNull(out); @@ -274,9 +239,6 @@ class ShaderTransformerTest { @Test void transformIsDeterministicUnderConcurrency() throws ExecutionException, InterruptedException { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); - final String vertex = "#version 120\nvoid main(){gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;}"; final String fragment = "#version 120\nvoid main(){gl_FragColor = vec4(1.0);}"; @@ -302,9 +264,6 @@ class ShaderTransformerTest { @Test void formattedShaderIsParseable() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); - final String vertex = "#version 120\nvoid main(){gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;}"; final String fragment = "#version 120\nvoid main(){gl_FragColor = vec4(1.0);}"; diff --git a/src/test/java/net/coderbot/iris/pipeline/transform/TransformTestSupport.java b/src/test/java/net/coderbot/iris/pipeline/transform/TransformTestSupport.java new file mode 100644 index 000000000..57bc06c68 --- /dev/null +++ b/src/test/java/net/coderbot/iris/pipeline/transform/TransformTestSupport.java @@ -0,0 +1,60 @@ +package net.coderbot.iris.pipeline.transform; + +import com.gtnewhorizons.angelica.glsm.RenderSystem; +import com.gtnewhorizons.angelica.glsm.testutil.Reflect; +import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability; +import org.taumc.glsl.ShaderParser; +import org.taumc.glsl.ShaderPrinter; +import org.taumc.glsl.Transformer; + +public final class TransformTestSupport { + + public static final InputAvailability TEX_LM = InputAvailability.of(true, true); + + private TransformTestSupport() {} + + /** Returns the previous value for {@link #restoreGlslVersion}. */ + public static int pinGlslVersion(int version) { + final int previous = Reflect.getStatic(RenderSystem.class, "maxGlslVersion"); + Reflect.setStatic(RenderSystem.class, "maxGlslVersion", version); + ShaderTransformer.init(); + return previous; + } + + public static void restoreGlslVersion(int previous) { + Reflect.setStatic(RenderSystem.class, "maxGlslVersion", previous); + clearCaches(); + ShaderTransformer.init(); + } + + public static void clearCaches() { + TransformPatcher.clearCache(); + ShaderTransformer.clearCache(); + } + + public static Transformer parse(String src) { + return new Transformer(ShaderParser.parseShader(src).full()); + } + + public static String format(Transformer t) { + final StringBuilder sb = new StringBuilder(); + t.mutateTree(tree -> sb.append(ShaderPrinter.getFormattedShader(tree))); + return sb.toString(); + } + + public static int occurrences(String haystack, String needle) { + int n = 0, i = 0; + while ((i = haystack.indexOf(needle, i)) != -1) { n++; i += needle.length(); } + return n; + } + + /** Whitespace runs to one space, trimmed. */ + public static String norm(String s) { + return s.replaceAll("\\s+", " ").trim(); + } + + /** All whitespace removed. */ + public static String squash(String s) { + return s.replaceAll("\\s+", ""); + } +} diff --git a/src/test/java/net/coderbot/iris/pipeline/transform/VertexReplayExtractionTest.java b/src/test/java/net/coderbot/iris/pipeline/transform/VertexReplayExtractionTest.java index 6540f0779..57a20481c 100644 --- a/src/test/java/net/coderbot/iris/pipeline/transform/VertexReplayExtractionTest.java +++ b/src/test/java/net/coderbot/iris/pipeline/transform/VertexReplayExtractionTest.java @@ -1,11 +1,9 @@ package net.coderbot.iris.pipeline.transform; -import com.gtnewhorizons.angelica.glsm.RenderSystem; import com.gtnewhorizons.angelica.glsm.ffp.InstancedAttribs; import com.gtnewhorizons.angelica.glsm.ffp.Instancing; -import com.gtnewhorizons.angelica.glsm.testutil.Reflect; -import net.coderbot.iris.gbuffer_overrides.matching.InputAvailability; import net.coderbot.iris.pipeline.transform.RwImageStoreExtractor.VertexInput; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -14,6 +12,7 @@ import java.util.EnumMap; import java.util.List; import java.util.Map; +import static net.coderbot.iris.pipeline.transform.TransformTestSupport.TEX_LM; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -58,18 +57,21 @@ class VertexReplayExtractionTest { void main() { gl_FragData[0] = texture2D(tex, texCoord); } """; - private static final InputAvailability TEX_LM = InputAvailability.of(true, true); + private static int previousMaxGlslVersion; @BeforeAll - static void initShaderTransformer() throws Exception { - Reflect.setStatic(RenderSystem.class, "maxGlslVersion", 460); - ShaderTransformer.init(); + static void pinGlslVersion() { + previousMaxGlslVersion = TransformTestSupport.pinGlslVersion(460); + } + + @AfterAll + static void restoreGlslVersion() { + TransformTestSupport.restoreGlslVersion(previousMaxGlslVersion); } @BeforeEach void clearCaches() { - TransformPatcher.clearCache(); - ShaderTransformer.clearCache(); + TransformTestSupport.clearCaches(); } private static RwImageStoreExtractor.Result replayOf(Map transformed) { @@ -193,7 +195,7 @@ class VertexReplayExtractionTest { gl_Position = iris_Vertex; } """); - final String compute = result.computeSource().replaceAll("\\s+", " "); + final String compute = TransformTestSupport.norm(result.computeSource()); assertTrue(compute.contains("void fill ( out float value )"), compute); assertFalse(compute.contains("out vec2 texCoord"), compute); } @@ -217,7 +219,7 @@ class VertexReplayExtractionTest { } """); assertNotNull(result); - final String compute = result.computeSource().replaceAll("\\s+", " "); + final String compute = TransformTestSupport.norm(result.computeSource()); assertTrue(compute.contains("layout ( std430 ) buffer ;"), compute); assertFalse(compute.contains("gl_PerVertex"), compute); assertTrue(compute.contains("struct Outputs { int id ; vec2 uv ; }; Outputs v ;"), compute); diff --git a/src/test/java/net/coderbot/iris/shadows/frustum/ShadowFrustumIntersectAabTest.java b/src/test/java/net/coderbot/iris/shadows/frustum/ShadowFrustumIntersectAabTest.java index bbacdc50d..055dcd518 100644 --- a/src/test/java/net/coderbot/iris/shadows/frustum/ShadowFrustumIntersectAabTest.java +++ b/src/test/java/net/coderbot/iris/shadows/frustum/ShadowFrustumIntersectAabTest.java @@ -3,7 +3,6 @@ package net.coderbot.iris.shadows.frustum; import net.coderbot.iris.shadows.frustum.advanced.AdvancedShadowCullingFrustum; import net.coderbot.iris.shadows.frustum.advanced.SafeZoneCullingFrustum; import net.coderbot.iris.shadows.frustum.fallback.BoxCullingFrustum; -import net.minecraft.client.renderer.culling.Frustrum; import org.embeddedt.embeddium.impl.render.viewport.frustum.Frustum; import org.joml.Matrix4f; import org.joml.Vector3f; @@ -17,7 +16,6 @@ import static net.coderbot.iris.shadows.frustum.ShadowFrustumTestSupport.forEach import static net.coderbot.iris.shadows.frustum.ShadowFrustumTestSupport.projection; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class ShadowFrustumIntersectAabTest { @@ -35,12 +33,6 @@ class ShadowFrustumIntersectAabTest { return frustum; } - @Test - void frustrumStubIsActive() { - assertThrows(NoSuchFieldException.class, () -> Frustrum.class.getDeclaredField("clippingHelper"), - "the real Frustrum's GL-touching clippingHelper field is on the classpath; the test stub did not win"); - } - @Test void boxCullingFrustumReportsTriState() { final BoxCullingFrustum frustum = new BoxCullingFrustum(new BoxCuller(64.0)); diff --git a/src/test/java/net/coderbot/iris/uniforms/PerFrameUniformBlockHarvesterTest.java b/src/test/java/net/coderbot/iris/uniforms/PerFrameUniformBlockHarvesterTest.java index 5374fe1e9..b38884a50 100644 --- a/src/test/java/net/coderbot/iris/uniforms/PerFrameUniformBlockHarvesterTest.java +++ b/src/test/java/net/coderbot/iris/uniforms/PerFrameUniformBlockHarvesterTest.java @@ -57,11 +57,6 @@ class PerFrameUniformBlockHarvesterTest { assertTrue(names.contains("shadowModelView"), "shadow matrices are pure functions of pack directives"); } - @Test - void orderIsDeterministic() { - assertEquals(blockNames(), blockNames()); - } - private static List perPassNames() { final PerFrameUniformBlock block = PerFrameUniformBlockHarvester.toPerPassBlock(manifest(), manifest()); return block.members().stream().map(PerFrameUniformBlock.Member::name).toList(); diff --git a/tracy-client/src/test/java/com/gtnewhorizons/angelica/tracy/TracyBindingSmokeTest.java b/tracy-client/src/test/java/com/gtnewhorizons/angelica/tracy/TracyBindingSmokeTest.java index 50ecc790b..a5439b389 100644 --- a/tracy-client/src/test/java/com/gtnewhorizons/angelica/tracy/TracyBindingSmokeTest.java +++ b/tracy-client/src/test/java/com/gtnewhorizons/angelica/tracy/TracyBindingSmokeTest.java @@ -36,19 +36,16 @@ class TracyBindingSmokeTest { private static TracyClientBackend backend; static boolean nativePresent() { - for (String platform : new String[] {"windows-x64", "linux-x64", "macos-x64", "macos-arm64"}) { - for (String lib : new String[] {"TracyClient.dll", "libTracyClient.so", "libTracyClient.dylib"}) { - if (TracyBindingSmokeTest.class.getResource("/natives/tracy/" + TracyTags.TRACY_VERSION + "/" + platform + "/" + lib) != null) { - return true; - } - } - } - return false; + return anyNativePresent("TracyClient.dll", "libTracyClient.so", "libTracyClient.dylib"); } static boolean captureLibPresent() { + return anyNativePresent("AngelicaTracyCapture.dll", "libAngelicaTracyCapture.so", "libAngelicaTracyCapture.dylib"); + } + + private static boolean anyNativePresent(String... libs) { for (String platform : new String[] {"windows-x64", "linux-x64", "macos-x64", "macos-arm64"}) { - for (String lib : new String[] {"AngelicaTracyCapture.dll", "libAngelicaTracyCapture.so", "libAngelicaTracyCapture.dylib"}) { + for (String lib : libs) { if (TracyBindingSmokeTest.class.getResource("/natives/tracy/" + TracyTags.TRACY_VERSION + "/" + platform + "/" + lib) != null) { return true; } diff --git a/tracy-client/src/test/java/com/gtnewhorizons/angelica/tracy/TracyPortsTest.java b/tracy-client/src/test/java/com/gtnewhorizons/angelica/tracy/TracyPortsTest.java index bfb6833d3..739e1b814 100644 --- a/tracy-client/src/test/java/com/gtnewhorizons/angelica/tracy/TracyPortsTest.java +++ b/tracy-client/src/test/java/com/gtnewhorizons/angelica/tracy/TracyPortsTest.java @@ -8,6 +8,7 @@ import java.net.ServerSocket; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; class TracyPortsTest { @@ -21,6 +22,7 @@ class TracyPortsTest { return; } catch (IOException ignored) {} } + assumeTrue(false, "no port in 8086..8105 could be bound"); } @Test diff --git a/tracy-client/src/test/java/com/gtnewhorizons/angelica/tracy/ZoneStackTest.java b/tracy-client/src/test/java/com/gtnewhorizons/angelica/tracy/ZoneStackTest.java index 1c9c7fdb5..7f0de92b8 100644 --- a/tracy-client/src/test/java/com/gtnewhorizons/angelica/tracy/ZoneStackTest.java +++ b/tracy-client/src/test/java/com/gtnewhorizons/angelica/tracy/ZoneStackTest.java @@ -30,13 +30,6 @@ class ZoneStackTest { assertEquals(ZoneStack.EMPTY, stack.pop()); } - @Test - void growsPastInitialCapacity() { - final ZoneStack stack = new ZoneStack(); - for (long i = 0; i < 256; i++) assertTrue(stack.push(i)); - for (long i = 255; i >= 0; i--) assertEquals(i, stack.pop()); - } - @Test void gpuFlagFollowsPop() { final ZoneStack stack = new ZoneStack();