Loading GPU/src/main/java/cz/fidentis/analyst/glsl/buffers/impl/SsboBufferImpl.java +1 −1 Original line number Diff line number Diff line Loading @@ -73,7 +73,7 @@ public class SsboBufferImpl extends AbstractBuffer implements SsboBuffer { gl.glNamedBufferData(getGlName(), items * getItemSize() + extraSpace, null, getUsage()); if (reset) { gl.glClearNamedBufferData(getGlName(), GL_R8, GL_RED, GL_UNSIGNED_BYTE, null); // zero buffer gl.glClearNamedBufferData(getGlName(), GL_R32F, GL_RED, GL_FLOAT, null); // zero buffer } } Loading GPU/src/main/resources/shaders/raycasting/GridParametersCS.glsl +1 −1 Original line number Diff line number Diff line Loading @@ -36,7 +36,7 @@ ivec3 locationToCell(vec3 loc) { Also estimates the optional cell size for the grid using heuristic. The min/max bounding corners and sum of the edge distances are computed using parallel reduction. This implementation works in-place and for arbitrary triangle array size. This implementation works in-place and for arbitrary triangle array size >= 2. This shader should be dispatched in multiple rounds and with work group size reduced with the half of the previous size. On last single invocation, results will be written to the grid info buffer. Loading GUI/src/main/java/cz/fidentis/analyst/gui/app/tools/RcDistanceGPUBenchmark.java +2 −2 Original line number Diff line number Diff line Loading @@ -192,8 +192,8 @@ public class RcDistanceGPUBenchmark { private static class MeasuredTimes { // these times are stored in nanosecond precision List<Long> buildTimes = new ArrayList<>(); List<Long> visitTimes = new ArrayList<>(); private List<Long> buildTimes = new ArrayList<>(); private List<Long> visitTimes = new ArrayList<>(); public long getTotalBuildTimeMs() { return buildTimes.stream().reduce(Long::sum).get() / 1000_000; // divide by 1000_000 to get a millis Loading GeometryEngines/src/main/java/cz/fidentis/analyst/engines/distance/impl/MeshDistanceRCGPU.java +6 −1 Original line number Diff line number Diff line Loading @@ -46,7 +46,7 @@ public class MeshDistanceRCGPU extends MeshDistanceVisitorImpl { * Creates 3D uniform grid and populates it with triangles from facets. * * @param context active OpenGL context on which makeCurrent() can be called now and during visit * @param mainFacets facets from which to take triangles * @param mainFacets facets from which to take triangles, at least two triangles must be present in total * @param relativeDistance If true, then the visitor calculates the relative distances with respect * to the normal vectors of source facets (normal vectors have to present), * i.e., we can get negative distances. Loading @@ -64,6 +64,10 @@ public class MeshDistanceRCGPU extends MeshDistanceVisitorImpl { int trianglesCount = mainFacets.stream().map(MeshFacet::getNumTriangles).reduce(Integer::sum).get(); int verticesCount = mainFacets.stream().map(MeshFacet::getNumberOfVertices).reduce(Integer::sum).get(); if(trianglesCount < 2) { throw new IllegalArgumentException("This GPU distance measurement method needs at least two triangles."); } fillBufferWithVertices(mainFacets, verticesCount); fillBufferWithTriangles(mainFacets, trianglesCount); Loading Loading @@ -194,6 +198,7 @@ public class MeshDistanceRCGPU extends MeshDistanceVisitorImpl { case GRID_PARAMETERS_GLSL_PROGRAM -> program.setNumberVar("invocations", invocations); // tells the shader the reduction direction upsweep/downsweep case GRID_CELL_BOUNDS_GLSL_PROGRAM -> program.setNumberVar("upsweep", reverse ? GL_FALSE : GL_TRUE); default -> {} } GlslServices.runProgram(context, (invocations / WORKGROUP_SIZE) + 1, 1, 1); Loading GeometryEngines/src/test/java/cz/fidentis/analyst/engines/distance/MeshDistanceRCGPUTest.java 0 → 100644 +124 −0 Original line number Diff line number Diff line package cz.fidentis.analyst.engines.distance; import com.jogamp.opengl.*; import cz.fidentis.analyst.data.mesh.MeshFacet; import cz.fidentis.analyst.data.mesh.MeshFactory; import cz.fidentis.analyst.data.mesh.impl.cornertable.CornerTableRow; import cz.fidentis.analyst.engines.distance.measurement.FacetDistances; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIf; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Tests GPU distance measurement. Runs only when GL capable window manager is present. * Results are compared to reference CPU implementation with defined error - note floats vs doubles. * * @author Pavol Kycina */ public class MeshDistanceRCGPUTest { /** this tells how much the GPU calculated distance can be different from CPU distance (note GPU floats vs CPU doubles)*/ private static final double ALLOWED_DISTANCE_ERROR = 0.01; protected static MeshFacet getTrivialTwoTriangleFacet(double offsetZ, double size) { MeshFacet facet = MeshFactory.createEmptyMeshFacet(); // these 6 points create one square with two triangles, no vertex sharing facet.addVertex(MeshFactory.createMeshPoint(new Point3d(-size, -size, offsetZ), new Vector3d(0, 0, 1), new Vector3d(), null)); facet.addVertex(MeshFactory.createMeshPoint(new Point3d(size, -size, offsetZ), new Vector3d(0, 0, 1), new Vector3d(), null)); facet.addVertex(MeshFactory.createMeshPoint(new Point3d(-size, size, offsetZ), new Vector3d(0, 0, 1), new Vector3d(), null)); facet.addVertex(MeshFactory.createMeshPoint(new Point3d(-size, size, offsetZ), new Vector3d(0, 0, 1), new Vector3d(), null)); facet.addVertex(MeshFactory.createMeshPoint(new Point3d(size, -size, offsetZ), new Vector3d(0, 0, 1), new Vector3d(), null)); facet.addVertex(MeshFactory.createMeshPoint(new Point3d(-size, size, offsetZ), new Vector3d(0, 0, 1), new Vector3d(), null)); facet.getCornerTable().addRow(new CornerTableRow(0, -1)); facet.getCornerTable().addRow(new CornerTableRow(1, -1)); facet.getCornerTable().addRow(new CornerTableRow(2, -1)); facet.getCornerTable().addRow(new CornerTableRow(3, -1)); facet.getCornerTable().addRow(new CornerTableRow(4, -1)); facet.getCornerTable().addRow(new CornerTableRow(5, -1)); return facet; } @Test @EnabledIf("isOpenGLAvailable") public void gpuDistanceTest() { GLAutoDrawable drawable = createDummyGLContext(); GLContext context = drawable.getContext(); // idea is that rays are shot from the smaller two triangles to the bigger two triangles ib the background // which should definitely result into intersections MeshFacet smallFacet = getTrivialTwoTriangleFacet(0, 1); MeshFacet normalFacet = getTrivialTwoTriangleFacet(1, 10); // CPU visit MeshDistanceVisitor cpuVisitor = new MeshDistanceConfig(MeshDistanceConfig.Method.RAY_CASTING, normalFacet, null, true, true).getVisitor(); smallFacet.accept(cpuVisitor); FacetDistances referenceDistances = cpuVisitor.getDistancesOfVisitedFacets().getFacetMeasurement(smallFacet); // GPU visit MeshDistanceVisitor gpuVisitor = new MeshDistanceConfig(MeshDistanceConfig.Method.RAY_CASTING_GPU, normalFacet, context, true, true).getVisitor(); smallFacet.accept(gpuVisitor); FacetDistances gpuDistances = gpuVisitor.getDistancesOfVisitedFacets().getFacetMeasurement(smallFacet); assertTrue(areEqual(referenceDistances, gpuDistances)); gpuVisitor.dispose(); drawable.destroy(); } /** * Tests two distance measurements if they have the same values with defined error. * @param cpuDistances first values * @param gpuDistances second values * @return true if they are the same (with error), false otherwise */ private static boolean areEqual(FacetDistances cpuDistances, FacetDistances gpuDistances) { if(cpuDistances.size() != gpuDistances.size()) { return false; } int matches = 0; for (int i = 0; i < cpuDistances.size(); i++) { double dGPU = cpuDistances.get(i).getDistance(); double dCPU = gpuDistances.get(i).getDistance(); // round the numbers because GPU uses floats if((Double.isInfinite(dGPU) && Double.isInfinite(dCPU)) || Math.abs(dGPU - dCPU) < ALLOWED_DISTANCE_ERROR) { matches++; }else { // uncomment for debugging //System.out.println("NOT MATCHING: (gpu) " + dGPU + " (cpu) " + dCPU + " i: " + i); } } return cpuDistances.size() == matches; } /** * Creates dummy GL drawable with its context. Available only on platforms with window manager. * @return initialized GL context */ private static GLAutoDrawable createDummyGLContext() { GLProfile glProfile = GLProfile.getDefault(); GLCapabilities glCapabilities = new GLCapabilities(glProfile); GLAutoDrawable drawable = GLDrawableFactory.getFactory(glProfile).createOffscreenAutoDrawable(null, glCapabilities, null, 1, 1); drawable.display(); // initialize context return drawable; } private static boolean isOpenGLAvailable() { try { GLProfile glProfile = GLProfile.getDefault(); return glProfile != null; }catch (java.lang.UnsatisfiedLinkError ignored) { return false; } } } Loading
GPU/src/main/java/cz/fidentis/analyst/glsl/buffers/impl/SsboBufferImpl.java +1 −1 Original line number Diff line number Diff line Loading @@ -73,7 +73,7 @@ public class SsboBufferImpl extends AbstractBuffer implements SsboBuffer { gl.glNamedBufferData(getGlName(), items * getItemSize() + extraSpace, null, getUsage()); if (reset) { gl.glClearNamedBufferData(getGlName(), GL_R8, GL_RED, GL_UNSIGNED_BYTE, null); // zero buffer gl.glClearNamedBufferData(getGlName(), GL_R32F, GL_RED, GL_FLOAT, null); // zero buffer } } Loading
GPU/src/main/resources/shaders/raycasting/GridParametersCS.glsl +1 −1 Original line number Diff line number Diff line Loading @@ -36,7 +36,7 @@ ivec3 locationToCell(vec3 loc) { Also estimates the optional cell size for the grid using heuristic. The min/max bounding corners and sum of the edge distances are computed using parallel reduction. This implementation works in-place and for arbitrary triangle array size. This implementation works in-place and for arbitrary triangle array size >= 2. This shader should be dispatched in multiple rounds and with work group size reduced with the half of the previous size. On last single invocation, results will be written to the grid info buffer. Loading
GUI/src/main/java/cz/fidentis/analyst/gui/app/tools/RcDistanceGPUBenchmark.java +2 −2 Original line number Diff line number Diff line Loading @@ -192,8 +192,8 @@ public class RcDistanceGPUBenchmark { private static class MeasuredTimes { // these times are stored in nanosecond precision List<Long> buildTimes = new ArrayList<>(); List<Long> visitTimes = new ArrayList<>(); private List<Long> buildTimes = new ArrayList<>(); private List<Long> visitTimes = new ArrayList<>(); public long getTotalBuildTimeMs() { return buildTimes.stream().reduce(Long::sum).get() / 1000_000; // divide by 1000_000 to get a millis Loading
GeometryEngines/src/main/java/cz/fidentis/analyst/engines/distance/impl/MeshDistanceRCGPU.java +6 −1 Original line number Diff line number Diff line Loading @@ -46,7 +46,7 @@ public class MeshDistanceRCGPU extends MeshDistanceVisitorImpl { * Creates 3D uniform grid and populates it with triangles from facets. * * @param context active OpenGL context on which makeCurrent() can be called now and during visit * @param mainFacets facets from which to take triangles * @param mainFacets facets from which to take triangles, at least two triangles must be present in total * @param relativeDistance If true, then the visitor calculates the relative distances with respect * to the normal vectors of source facets (normal vectors have to present), * i.e., we can get negative distances. Loading @@ -64,6 +64,10 @@ public class MeshDistanceRCGPU extends MeshDistanceVisitorImpl { int trianglesCount = mainFacets.stream().map(MeshFacet::getNumTriangles).reduce(Integer::sum).get(); int verticesCount = mainFacets.stream().map(MeshFacet::getNumberOfVertices).reduce(Integer::sum).get(); if(trianglesCount < 2) { throw new IllegalArgumentException("This GPU distance measurement method needs at least two triangles."); } fillBufferWithVertices(mainFacets, verticesCount); fillBufferWithTriangles(mainFacets, trianglesCount); Loading Loading @@ -194,6 +198,7 @@ public class MeshDistanceRCGPU extends MeshDistanceVisitorImpl { case GRID_PARAMETERS_GLSL_PROGRAM -> program.setNumberVar("invocations", invocations); // tells the shader the reduction direction upsweep/downsweep case GRID_CELL_BOUNDS_GLSL_PROGRAM -> program.setNumberVar("upsweep", reverse ? GL_FALSE : GL_TRUE); default -> {} } GlslServices.runProgram(context, (invocations / WORKGROUP_SIZE) + 1, 1, 1); Loading
GeometryEngines/src/test/java/cz/fidentis/analyst/engines/distance/MeshDistanceRCGPUTest.java 0 → 100644 +124 −0 Original line number Diff line number Diff line package cz.fidentis.analyst.engines.distance; import com.jogamp.opengl.*; import cz.fidentis.analyst.data.mesh.MeshFacet; import cz.fidentis.analyst.data.mesh.MeshFactory; import cz.fidentis.analyst.data.mesh.impl.cornertable.CornerTableRow; import cz.fidentis.analyst.engines.distance.measurement.FacetDistances; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIf; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Tests GPU distance measurement. Runs only when GL capable window manager is present. * Results are compared to reference CPU implementation with defined error - note floats vs doubles. * * @author Pavol Kycina */ public class MeshDistanceRCGPUTest { /** this tells how much the GPU calculated distance can be different from CPU distance (note GPU floats vs CPU doubles)*/ private static final double ALLOWED_DISTANCE_ERROR = 0.01; protected static MeshFacet getTrivialTwoTriangleFacet(double offsetZ, double size) { MeshFacet facet = MeshFactory.createEmptyMeshFacet(); // these 6 points create one square with two triangles, no vertex sharing facet.addVertex(MeshFactory.createMeshPoint(new Point3d(-size, -size, offsetZ), new Vector3d(0, 0, 1), new Vector3d(), null)); facet.addVertex(MeshFactory.createMeshPoint(new Point3d(size, -size, offsetZ), new Vector3d(0, 0, 1), new Vector3d(), null)); facet.addVertex(MeshFactory.createMeshPoint(new Point3d(-size, size, offsetZ), new Vector3d(0, 0, 1), new Vector3d(), null)); facet.addVertex(MeshFactory.createMeshPoint(new Point3d(-size, size, offsetZ), new Vector3d(0, 0, 1), new Vector3d(), null)); facet.addVertex(MeshFactory.createMeshPoint(new Point3d(size, -size, offsetZ), new Vector3d(0, 0, 1), new Vector3d(), null)); facet.addVertex(MeshFactory.createMeshPoint(new Point3d(-size, size, offsetZ), new Vector3d(0, 0, 1), new Vector3d(), null)); facet.getCornerTable().addRow(new CornerTableRow(0, -1)); facet.getCornerTable().addRow(new CornerTableRow(1, -1)); facet.getCornerTable().addRow(new CornerTableRow(2, -1)); facet.getCornerTable().addRow(new CornerTableRow(3, -1)); facet.getCornerTable().addRow(new CornerTableRow(4, -1)); facet.getCornerTable().addRow(new CornerTableRow(5, -1)); return facet; } @Test @EnabledIf("isOpenGLAvailable") public void gpuDistanceTest() { GLAutoDrawable drawable = createDummyGLContext(); GLContext context = drawable.getContext(); // idea is that rays are shot from the smaller two triangles to the bigger two triangles ib the background // which should definitely result into intersections MeshFacet smallFacet = getTrivialTwoTriangleFacet(0, 1); MeshFacet normalFacet = getTrivialTwoTriangleFacet(1, 10); // CPU visit MeshDistanceVisitor cpuVisitor = new MeshDistanceConfig(MeshDistanceConfig.Method.RAY_CASTING, normalFacet, null, true, true).getVisitor(); smallFacet.accept(cpuVisitor); FacetDistances referenceDistances = cpuVisitor.getDistancesOfVisitedFacets().getFacetMeasurement(smallFacet); // GPU visit MeshDistanceVisitor gpuVisitor = new MeshDistanceConfig(MeshDistanceConfig.Method.RAY_CASTING_GPU, normalFacet, context, true, true).getVisitor(); smallFacet.accept(gpuVisitor); FacetDistances gpuDistances = gpuVisitor.getDistancesOfVisitedFacets().getFacetMeasurement(smallFacet); assertTrue(areEqual(referenceDistances, gpuDistances)); gpuVisitor.dispose(); drawable.destroy(); } /** * Tests two distance measurements if they have the same values with defined error. * @param cpuDistances first values * @param gpuDistances second values * @return true if they are the same (with error), false otherwise */ private static boolean areEqual(FacetDistances cpuDistances, FacetDistances gpuDistances) { if(cpuDistances.size() != gpuDistances.size()) { return false; } int matches = 0; for (int i = 0; i < cpuDistances.size(); i++) { double dGPU = cpuDistances.get(i).getDistance(); double dCPU = gpuDistances.get(i).getDistance(); // round the numbers because GPU uses floats if((Double.isInfinite(dGPU) && Double.isInfinite(dCPU)) || Math.abs(dGPU - dCPU) < ALLOWED_DISTANCE_ERROR) { matches++; }else { // uncomment for debugging //System.out.println("NOT MATCHING: (gpu) " + dGPU + " (cpu) " + dCPU + " i: " + i); } } return cpuDistances.size() == matches; } /** * Creates dummy GL drawable with its context. Available only on platforms with window manager. * @return initialized GL context */ private static GLAutoDrawable createDummyGLContext() { GLProfile glProfile = GLProfile.getDefault(); GLCapabilities glCapabilities = new GLCapabilities(glProfile); GLAutoDrawable drawable = GLDrawableFactory.getFactory(glProfile).createOffscreenAutoDrawable(null, glCapabilities, null, 1, 1); drawable.display(); // initialize context return drawable; } private static boolean isOpenGLAvailable() { try { GLProfile glProfile = GLProfile.getDefault(); return glProfile != null; }catch (java.lang.UnsatisfiedLinkError ignored) { return false; } } }